Merge "Remove use of private SkBlurMask.h"
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index f3f4fa1..e2fff0f 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -862,7 +862,8 @@
         public boolean KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED =
                 DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED;
 
-        public int USE_TARE_POLICY = Settings.Global.DEFAULT_ENABLE_TARE;
+        public int USE_TARE_POLICY = EconomyManager.DEFAULT_ENABLE_POLICY_ALARM
+                ? EconomyManager.DEFAULT_ENABLE_TARE_MODE : EconomyManager.ENABLED_MODE_OFF;
 
         /**
          * The amount of temporary reserve quota to give apps on receiving the
@@ -1294,7 +1295,8 @@
                     KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED);
             pw.println();
 
-            pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY);
+            pw.print(Settings.Global.ENABLE_TARE,
+                    EconomyManager.enabledModeToString(USE_TARE_POLICY));
             pw.println();
 
             pw.print(KEY_TEMPORARY_QUOTA_BUMP, TEMPORARY_QUOTA_BUMP);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 6a7904c..70bf353 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -636,7 +636,6 @@
         public static final long DEFAULT_RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS =
                 Math.min(Long.MAX_VALUE, DEFAULT_RUNTIME_USER_INITIATED_LIMIT_MS);
         static final boolean DEFAULT_PERSIST_IN_SPLIT_FILES = true;
-        private static final boolean DEFAULT_USE_TARE_POLICY = false;
 
         /**
          * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
@@ -802,7 +801,8 @@
         /**
          * If true, use TARE policy for job limiting. If false, use quotas.
          */
-        public boolean USE_TARE_POLICY = DEFAULT_USE_TARE_POLICY;
+        public boolean USE_TARE_POLICY = EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER
+                && EconomyManager.DEFAULT_ENABLE_TARE_MODE == EconomyManager.ENABLED_MODE_ON;
 
         private void updateBatchingConstantsLocked() {
             MIN_READY_NON_ACTIVE_JOBS_COUNT = DeviceConfig.getInt(
diff --git a/api/Android.bp b/api/Android.bp
index f49e6dd..78ddc6a 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -184,6 +184,18 @@
         "$(location :frameworks-base-api-module-lib-current.txt)",
 }
 
+genrule {
+    name: "frameworks-base-api-current.srcjar",
+    tools: ["merge_zips"],
+    out: ["current.srcjar"],
+    cmd: "$(location merge_zips) $(out) $(in)",
+    srcs: [
+        ":api-stubs-docs-non-updatable",
+        ":all-modules-public-stubs-source",
+    ],
+    visibility: ["//visibility:private"], // Used by make module in //development, mind
+}
+
 // This produces the same annotations.zip as framework-doc-stubs, but by using
 // outputs from individual modules instead of all the source code.
 genrule_defaults {
diff --git a/api/api.go b/api/api.go
index c91ff81..077ab96 100644
--- a/api/api.go
+++ b/api/api.go
@@ -147,17 +147,6 @@
 	ctx.CreateModule(genrule.GenRuleFactory, &props)
 }
 
-func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) {
-	props := genruleProps{}
-	props.Name = proptools.StringPtr(ctx.ModuleName() + "-current.srcjar")
-	props.Tools = []string{"merge_zips"}
-	props.Out = []string{"current.srcjar"}
-	props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)")
-	props.Srcs = append([]string{":api-stubs-docs-non-updatable"}, createSrcs(modules, "{.public.stubs.source}")...)
-	props.Visibility = []string{"//visibility:private"} // Used by make module in //development, mind
-	ctx.CreateModule(genrule.GenRuleFactory, &props)
-}
-
 func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) {
 	for _, i := range []struct{
 		name    string
@@ -382,8 +371,6 @@
 	}
 	createMergedTxts(ctx, bootclasspath, system_server_classpath)
 
-	createMergedStubsSrcjar(ctx, bootclasspath)
-
 	createMergedPublicStubs(ctx, bootclasspath)
 	createMergedSystemStubs(ctx, bootclasspath)
 	createMergedTestStubsForNonUpdatableModules(ctx)
diff --git a/core/api/current.txt b/core/api/current.txt
index 3eb543d..1aad4b7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1857,6 +1857,7 @@
     field public static final int windowMinWidthMajor = 16843606; // 0x1010356
     field public static final int windowMinWidthMinor = 16843607; // 0x1010357
     field public static final int windowNoDisplay = 16843294; // 0x101021e
+    field public static final int windowNoMoveAnimation;
     field public static final int windowNoTitle = 16842838; // 0x1010056
     field @Deprecated public static final int windowOverscan = 16843727; // 0x10103cf
     field public static final int windowReenterTransition = 16843951; // 0x10104af
@@ -4510,6 +4511,7 @@
     method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
     method public final void runOnUiThread(Runnable);
     method public void setActionBar(@Nullable android.widget.Toolbar);
+    method public void setAllowCrossUidActivitySwitchFromBelow(boolean);
     method public void setContentTransitionManager(android.transition.TransitionManager);
     method public void setContentView(@LayoutRes int);
     method public void setContentView(android.view.View);
@@ -24425,7 +24427,7 @@
     method public void registerTransferCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.TransferCallback);
     method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
     method public void setRouteListingPreference(@Nullable android.media.RouteListingPreference);
-    method public void showSystemOutputSwitcher();
+    method public boolean showSystemOutputSwitcher();
     method public void stop();
     method public void transferTo(@NonNull android.media.MediaRoute2Info);
     method public void unregisterControllerCallback(@NonNull android.media.MediaRouter2.ControllerCallback);
@@ -41244,6 +41246,7 @@
     method public void setUiEnabled(boolean);
     method public void show(android.os.Bundle, int);
     method public void startAssistantActivity(android.content.Intent);
+    method public void startAssistantActivity(@NonNull android.content.Intent, @NonNull android.os.Bundle);
     method public void startVoiceActivity(android.content.Intent);
     method public final void unregisterVisibleActivityCallback(@NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
     field public static final String KEY_SHOW_SESSION_ID = "android.service.voice.SHOW_SESSION_ID";
@@ -53823,6 +53826,7 @@
     ctor public WindowManager.LayoutParams(int, int, int, int, int, int, int);
     ctor public WindowManager.LayoutParams(android.os.Parcel);
     method public boolean areWallpaperTouchEventsEnabled();
+    method public boolean canPlayMoveAnimation();
     method public final int copyFrom(android.view.WindowManager.LayoutParams);
     method public String debug(String);
     method public int describeContents();
@@ -53835,6 +53839,7 @@
     method public boolean isHdrConversionEnabled();
     method public static boolean mayUseInputMethod(int);
     method public void setBlurBehindRadius(@IntRange(from=0) int);
+    method public void setCanPlayMoveAnimation(boolean);
     method public void setColorMode(int);
     method public void setFitInsetsIgnoringVisibility(boolean);
     method public void setFitInsetsSides(int);
@@ -55942,7 +55947,7 @@
 
   public final class TextBoundsInfoResult {
     ctor public TextBoundsInfoResult(int);
-    ctor public TextBoundsInfoResult(int, @NonNull android.view.inputmethod.TextBoundsInfo);
+    ctor public TextBoundsInfoResult(int, @Nullable android.view.inputmethod.TextBoundsInfo);
     method public int getResultCode();
     method @Nullable public android.view.inputmethod.TextBoundsInfo getTextBoundsInfo();
     field public static final int CODE_CANCELLED = 3; // 0x3
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index dde964c..03be333 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2800,6 +2800,12 @@
     ctor public VisibleActivityInfo(int, @NonNull android.os.IBinder);
   }
 
+  public class VoiceInteractionService extends android.app.Service {
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(@NonNull String, @NonNull java.util.Locale, @NonNull android.hardware.soundtrigger.SoundTrigger.ModuleProperties, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.AlwaysOnHotwordDetector.Callback);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(@NonNull String, @NonNull java.util.Locale, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull android.hardware.soundtrigger.SoundTrigger.ModuleProperties, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.AlwaysOnHotwordDetector.Callback);
+    method @NonNull public final java.util.List<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> listModuleProperties();
+  }
+
   public static class VoiceInteractionSession.ActivityId {
     method @NonNull public android.os.IBinder getAssistToken();
     method public int getTaskId();
@@ -3288,6 +3294,7 @@
 
   public interface WindowManager extends android.view.ViewManager {
     method public default int getDisplayImePolicy(int);
+    method public static boolean hasWindowExtensionsEnabled();
     method public default void holdLock(android.os.IBinder, int);
     method public default boolean isGlobalKey(int);
     method public default boolean isTaskSnapshotSupported();
@@ -3306,7 +3313,6 @@
     method public final void setWindowContextToken(@NonNull android.os.IBinder);
     field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
     field public static final int FLAG_SLIPPERY = 536870912; // 0x20000000
-    field public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 64; // 0x40
     field public CharSequence accessibilityTitle;
     field public int privateFlags;
   }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 1d03d84..d9c7a27 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -9193,6 +9193,24 @@
     }
 
     /**
+     * Specifies whether the activities below this one in the task can also start other activities
+     * or finish the task.
+     * <p>
+     * Starting from Target SDK Level {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, apps
+     * are blocked from starting new activities or finishing their task unless the top activity of
+     * such task belong to the same UID for security reasons.
+     * <p>
+     * Setting this flag to {@code true} will allow the launching app to ignore the restriction if
+     * this activity is on top. Apps matching the UID of this activity are always exempt.
+     *
+     * @param allowed {@code true} to disable the UID restrictions; {@code false} to revert back to
+     *                            the default behaviour
+     */
+    public void setAllowCrossUidActivitySwitchFromBelow(boolean allowed) {
+        ActivityClient.getInstance().setAllowCrossUidActivitySwitchFromBelow(mToken, allowed);
+    }
+
+    /**
      * Registers remote animations per transition type for this activity.
      *
      * @param definition The remote animation definition that defines which transition whould run
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 5354a44..44327af 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -478,6 +478,14 @@
         }
     }
 
+    void setAllowCrossUidActivitySwitchFromBelow(IBinder token, boolean allowed) {
+        try {
+            getActivityClientController().setAllowCrossUidActivitySwitchFromBelow(token, allowed);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
     int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
         try {
             return getActivityClientController().setVrMode(token, enabled, packageName);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 543c5e7..b50245d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2251,12 +2251,18 @@
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case BIND_SERVICE:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
+                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind: "
+                                + String.valueOf(msg.obj));
+                    }
                     handleBindService((BindServiceData)msg.obj);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case UNBIND_SERVICE:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
+                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind: "
+                                + String.valueOf(msg.obj));
+                    }
                     handleUnbindService((BindServiceData)msg.obj);
                     schedulePurgeIdler();
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -2270,13 +2276,19 @@
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case STOP_SERVICE:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
+                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop: "
+                                + String.valueOf(msg.obj));
+                    }
                     handleStopService((IBinder)msg.obj);
                     schedulePurgeIdler();
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case TIMEOUT_SERVICE:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceTimeout");
+                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceTimeout: "
+                                + String.valueOf(msg.obj));
+                    }
                     handleTimeoutService((IBinder) msg.obj, msg.arg1);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
@@ -2288,7 +2300,12 @@
                     cci.context.performFinalCleanup(cci.who, cci.what);
                     break;
                 case GC_WHEN_IDLE:
-                    scheduleGcIdler();
+                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "gcWhenIdle");
+                    try {
+                        scheduleGcIdler();
+                    } finally {
+                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                    }
                     break;
                 case DUMP_SERVICE:
                     handleDumpService((DumpComponentInfo)msg.obj);
@@ -2365,7 +2382,15 @@
                     handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1);
                     break;
                 case INSTALL_PROVIDER:
-                    handleInstallProvider((ProviderInfo) msg.obj);
+                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerInstall: "
+                                + String.valueOf(msg.obj));
+                    }
+                    try {
+                        handleInstallProvider((ProviderInfo) msg.obj);
+                    } finally {
+                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                    }
                     break;
                 case ON_NEW_ACTIVITY_OPTIONS:
                     Pair<IBinder, ActivityOptions> pair = (Pair<IBinder, ActivityOptions>) msg.obj;
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 5e40399..5136b20 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -120,6 +120,7 @@
     oneway void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
     oneway void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
     oneway void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
+    oneway void setAllowCrossUidActivitySwitchFromBelow(in IBinder token, boolean allowed);
     oneway void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
     oneway void overrideActivityTransition(IBinder token, boolean open, int enterAnim, int exitAnim,
             int backgroundColor);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index d207688..aa5b866 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -823,6 +823,7 @@
 
     /** Blocks until all broadcast queues become idle. */
     void waitForBroadcastIdle();
+    void waitForBroadcastBarrier();
 
     /** Delays delivering broadcasts to the specified package. */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DUMP)")
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 90ed4ce..f3ad9d1 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -368,8 +368,8 @@
      *   <li>App has a UID &lt; {@link android.os.Process#FIRST_APPLICATION_UID}</li>
      *   <li>App is on Doze allowlist</li>
      *   <li>Device is running in <a href="https://android.googlesource.com/platform/frameworks/base/+/master/packages/SystemUI/docs/demo_mode.md">Demo Mode</a></li>
-     *   <li><a href="https://source.android.com/devices/tech/admin/provision">Device owner app</a><li>
-     *   <li><a href="https://source.android.com/devices/tech/admin/managed-profiles">Profile owner apps</a><li>
+     *   <li><a href="https://source.android.com/devices/tech/admin/provision">Device owner app</a></li>
+     *   <li><a href="https://source.android.com/devices/tech/admin/managed-profiles">Profile owner apps</a></li>
      *   <li>Persistent apps</li>
      *   <li><a href="https://source.android.com/docs/core/connect/carrier">Carrier privileged apps</a></li>
      *   <li>Apps that have the {@code android.app.role.RoleManager#ROLE_EMERGENCY} role</li>
diff --git a/core/java/android/credentials/OWNERS b/core/java/android/credentials/OWNERS
index e8f393e..f5de01d 100644
--- a/core/java/android/credentials/OWNERS
+++ b/core/java/android/credentials/OWNERS
@@ -3,3 +3,5 @@
 sgjerry@google.com
 leecam@google.com
 akaphle@google.com
+omerozer@google.com
+jwillcox@google.com
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
index 1b661e7..d681a2c 100644
--- a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
@@ -74,6 +74,9 @@
     private static final String DPD_DELAY_SEC_KEY = "DPD_DELAY_SEC_KEY";
     private static final String NATT_KEEPALIVE_DELAY_SEC_KEY = "NATT_KEEPALIVE_DELAY_SEC_KEY";
     private static final String IKE_OPTIONS_KEY = "IKE_OPTIONS_KEY";
+    private static final String IP_VERSION_KEY = "IP_VERSION_KEY";
+    private static final String ENCAP_TYPE_KEY = "ENCAP_TYPE_KEY";
+    // TODO: add DSCP_KEY and IS_IKE_FRAGMENT_SUPPORTED_KEY.
 
     // TODO: b/243181760 Use the IKE API when they are exposed
     @VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -156,6 +159,8 @@
         result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds());
         result.putInt(DPD_DELAY_SEC_KEY, params.getDpdDelaySeconds());
         result.putInt(NATT_KEEPALIVE_DELAY_SEC_KEY, params.getNattKeepAliveDelaySeconds());
+        result.putInt(IP_VERSION_KEY, params.getIpVersion());
+        result.putInt(ENCAP_TYPE_KEY, params.getEncapType());
 
         // TODO: b/185941731 Make sure IkeSessionParamsUtils is automatically updated when a new
         // IKE_OPTION is defined in IKE module and added in the IkeSessionParams
@@ -207,6 +212,8 @@
                 in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
         builder.setDpdDelaySeconds(in.getInt(DPD_DELAY_SEC_KEY));
         builder.setNattKeepAliveDelaySeconds(in.getInt(NATT_KEEPALIVE_DELAY_SEC_KEY));
+        builder.setIpVersion(in.getInt(IP_VERSION_KEY));
+        builder.setEncapType(in.getInt(ENCAP_TYPE_KEY));
 
         final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY);
         Objects.requireNonNull(configReqListBundle, "Config request list was null");
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7f651c5..54e4909 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -40,7 +40,6 @@
 import android.app.NotificationManager;
 import android.app.SearchManager;
 import android.app.WallpaperManager;
-import android.app.tare.EconomyManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -14720,37 +14719,15 @@
                 = "forced_app_standby_for_small_battery_enabled";
 
         /**
-         * Whether to enable the TARE subsystem as a whole or not.
-         * 1 means enable, 0 means disable.
+         * Whether to enable the TARE subsystem or not.
+         * Valid values are
+         * {@link android.app.tare.EconomyManager#ENABLE_TARE_ON EconomyManager.ENABLE_TARE_*}.
          *
          * @hide
          */
         public static final String ENABLE_TARE = "enable_tare";
 
         /**
-         * Default value for {@link #ENABLE_TARE}.
-         *
-         * @hide
-         */
-        public static final int DEFAULT_ENABLE_TARE = EconomyManager.DEFAULT_ENABLE_TARE_MODE;
-
-        /**
-         * Whether to enable the TARE AlarmManager economic policy or not.
-         * 1 means enable, 0 means disable.
-         *
-         * @hide
-         */
-        public static final String ENABLE_TARE_ALARM_MANAGER = "enable_tare_alarm_manager";
-
-        /**
-         * Default value for {@link #ENABLE_TARE_ALARM_MANAGER}.
-         *
-         * @hide
-         */
-        public static final int DEFAULT_ENABLE_TARE_ALARM_MANAGER =
-                        EconomyManager.DEFAULT_ENABLE_POLICY_ALARM ? 1 : 0;
-
-        /**
          * Settings for AlarmManager's TARE EconomicPolicy (list of its economic factors).
          *
          * Keys are listed in {@link android.app.tare.EconomyManager}.
@@ -14760,22 +14737,6 @@
         public static final String TARE_ALARM_MANAGER_CONSTANTS = "tare_alarm_manager_constants";
 
         /**
-         * Whether to enable the TARE JobScheduler economic policy or not.
-         * 1 means enable, 0 means disable.
-         *
-         * @hide
-         */
-        public static final String ENABLE_TARE_JOB_SCHEDULER = "enable_tare_job_scheduler";
-
-        /**
-         * Default value for {@link #ENABLE_TARE_JOB_SCHEDULER}.
-         *
-         * @hide
-         */
-        public static final int DEFAULT_ENABLE_TARE_JOB_SCHEDULER =
-                EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER ? 1 : 0;
-
-        /**
          * Settings for JobScheduler's TARE EconomicPolicy (list of its economic factors).
          *
          * Keys are listed in {@link android.app.tare.EconomyManager}.
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index d957029..94384b0 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -70,8 +70,8 @@
  * <li>{@link #onTileAdded()} and {@link #onTileRemoved()} may be called outside of the
  * {@link #onCreate()} - {@link #onDestroy()} window</li>
  * </ul>
- * <p>TileService will be detected by tiles that match the {@value #ACTION_QS_TILE}
- * and require the permission "android.permission.BIND_QUICK_SETTINGS_TILE".
+ * <p>TileService will resolve against services that match the {@value #ACTION_QS_TILE} action
+ * and require the permission {@code android.permission.BIND_QUICK_SETTINGS_TILE}.
  * The label and icon for the service will be used as the default label and
  * icon for the tile. Here is an example TileService declaration.</p>
  * <pre class="prettyprint">
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index bcc3f58..48b7a59 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -784,8 +784,13 @@
         mSupportSandboxedDetectionService = supportSandboxedDetectionService;
     }
 
+    // Do nothing. This method should not be abstract.
+    // TODO (b/269355519) un-subclass AOHD.
     @Override
-    void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
+    void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {}
+
+    void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory,
+            @Nullable SoundTrigger.ModuleProperties moduleProperties) {
         if (mSupportSandboxedDetectionService) {
             initAndVerifyDetector(options, sharedMemory, mInternalCallback,
                     DETECTOR_TYPE_TRUSTED_HOTWORD_DSP);
@@ -793,9 +798,18 @@
         try {
             Identity identity = new Identity();
             identity.packageName = ActivityThread.currentOpPackageName();
+            if (moduleProperties == null) {
+                List<SoundTrigger.ModuleProperties> modulePropList =
+                        mModelManagementService.listModuleProperties(identity);
+                if (modulePropList.size() > 0) {
+                    moduleProperties = modulePropList.get(0);
+                }
+                // (@atneya) intentionally let a null moduleProperties through until
+                // all CTS tests are fixed
+            }
             mSoundTriggerSession =
                     mModelManagementService.createSoundTriggerSessionAsOriginator(
-                            identity, mBinder);
+                            identity, mBinder, moduleProperties);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 12853cb..a684e41 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -24,6 +24,8 @@
 import android.annotation.SdkConstant;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.ActivityThread;
 import android.app.Service;
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
@@ -33,7 +35,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
+import android.hardware.soundtrigger.SoundTrigger;
 import android.media.voice.KeyphraseModelManager;
+import android.media.permission.Identity;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -386,6 +390,22 @@
     }
 
     /**
+     * List available ST modules to attach to for test purposes.
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public final List<SoundTrigger.ModuleProperties> listModuleProperties() {
+        Identity identity = new Identity();
+        identity.packageName = ActivityThread.currentOpPackageName();
+        try {
+            return mSystemService.listModuleProperties(identity);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
      * This instance must be retained and used by the client.
      * Calling this a second time invalidates the previously created hotword detector
@@ -422,7 +442,8 @@
             @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
         return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
                 /* supportHotwordDetectionService= */ false, /* options= */ null,
-                /* sharedMemory= */ null, /* executor= */ null, callback);
+                /* sharedMemory= */ null, /* moduleProperties */ null,
+                /* executor= */ null, callback);
     }
 
     /**
@@ -461,10 +482,37 @@
         Objects.requireNonNull(callback);
         return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
                 /* supportHotwordDetectionService= */ false, /* options= */ null,
-                /* sharedMemory= */ null, executor, callback);
+                /* sharedMemory= */ null, /* moduleProperties */ null, executor, callback);
     }
 
     /**
+     * Same as {@link createAlwaysOnHotwordDetector(String, Locale, Executor,
+     * AlwaysOnHotwordDetector.Callback)}, but allow explicit selection of the underlying ST
+     * module to attach to.
+     * Use {@link listModuleProperties} to get available modules to attach to.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION)
+    @NonNull
+    public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(
+            @NonNull String keyphrase, @SuppressLint("UseIcu") @NonNull Locale locale,
+            @NonNull SoundTrigger.ModuleProperties moduleProperties,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AlwaysOnHotwordDetector.Callback callback) {
+
+        Objects.requireNonNull(keyphrase);
+        Objects.requireNonNull(locale);
+        Objects.requireNonNull(moduleProperties);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+                /* supportHotwordDetectionService= */ false, /* options= */ null,
+                /* sharedMemory= */ null, moduleProperties, executor, callback);
+    }
+
+
+    /**
      * Create an {@link AlwaysOnHotwordDetector} and trigger a {@link HotwordDetectionService}
      * service, then it will also pass the read-only data to hotword detection service.
      *
@@ -515,7 +563,7 @@
             @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
         return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
                 /* supportHotwordDetectionService= */ true, options, sharedMemory,
-                /* executor= */ null, callback);
+                /* modulProperties */ null, /* executor= */ null, callback);
     }
 
     /**
@@ -566,16 +614,46 @@
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
         return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
-                /* supportHotwordDetectionService= */ true, options, sharedMemory, executor,
-                callback);
+                /* supportHotwordDetectionService= */ true, options, sharedMemory,
+                /* moduleProperties */ null, executor, callback);
     }
 
+    /**
+     * Same as {@link createAlwaysOnHotwordDetector(String, Locale,
+     * PersistableBundle, SharedMemory, Executor, AlwaysOnHotwordDetector.Callback)},
+     * but allow explicit selection of the underlying ST module to attach to.
+     * Use {@link listModuleProperties} to get available modules to attach to.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION)
+    @NonNull
+    public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(
+            @NonNull String keyphrase, @SuppressLint("UseIcu") @NonNull Locale locale,
+            @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory,
+            @NonNull SoundTrigger.ModuleProperties moduleProperties,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AlwaysOnHotwordDetector.Callback callback) {
+
+        Objects.requireNonNull(keyphrase);
+        Objects.requireNonNull(locale);
+        Objects.requireNonNull(moduleProperties);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+                /* supportHotwordDetectionService= */ true, options, sharedMemory,
+                moduleProperties, executor, callback);
+    }
+
+
+
     private AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorInternal(
             @SuppressLint("MissingNullability") String keyphrase,  // TODO: nullability properly
             @SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
             boolean supportHotwordDetectionService,
             @Nullable PersistableBundle options,
             @Nullable SharedMemory sharedMemory,
+            @Nullable SoundTrigger.ModuleProperties moduleProperties,
             @Nullable @CallbackExecutor Executor executor,
             @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
 
@@ -609,7 +687,8 @@
 
             try {
                 dspDetector.registerOnDestroyListener(this::onHotwordDetectorDestroyed);
-                dspDetector.initialize(options, sharedMemory);
+                // If moduleProperties is null, the default STModule is used.
+                dspDetector.initialize(options, sharedMemory, moduleProperties);
             } catch (Exception e) {
                 mActiveDetectors.remove(dspDetector);
                 dspDetector.destroy();
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 5778518..cabcae3 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -26,6 +26,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.app.Activity;
+import android.app.ActivityOptions;
 import android.app.Dialog;
 import android.app.DirectAction;
 import android.app.Instrumentation;
@@ -1527,8 +1528,34 @@
      * <p>By default, the system will create a window for the UI for this session.  If you are using
      * an assistant activity instead, then you can disable the window creation by calling
      * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
+     *
+     * NOTE: if the app would like to override some options to start the Activity,
+     * use {@link #startAssistantActivity(Intent, Bundle)} instead.
      */
     public void startAssistantActivity(Intent intent) {
+        startAssistantActivity(intent, ActivityOptions.makeBasic().toBundle());
+    }
+
+    /**
+     * <p>Ask that a new assistant activity be started.  This will create a new task in the
+     * in activity manager: this means that
+     * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
+     * will be set for you to make it a new task.</p>
+     *
+     * <p>The newly started activity will be displayed on top of other activities in the system
+     * in a new layer that is not affected by multi-window mode.  Tasks started from this activity
+     * will go into the normal activity layer and not this new layer.</p>
+     *
+     * <p>By default, the system will create a window for the UI for this session.  If you are using
+     * an assistant activity instead, then you can disable the window creation by calling
+     * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
+     *
+     * @param intent the intent used to start an assistant activity
+     * @param bundle Additional options for how the Activity should be started. See
+     * {@link ActivityOptions} for how to build the Bundle supplied here.
+     */
+    public void startAssistantActivity(@NonNull Intent intent, @NonNull Bundle bundle) {
+        Objects.requireNonNull(bundle);
         if (mToken == null) {
             throw new IllegalStateException("Can't call before onCreate()");
         }
@@ -1537,7 +1564,7 @@
             intent.prepareToLeaveProcess(mContext);
             int res = mSystemService.startAssistantActivity(mToken, intent,
                     intent.resolveType(mContext.getContentResolver()),
-                    mContext.getAttributionTag());
+                    mContext.getAttributionTag(), bundle);
             Instrumentation.checkStartActivityResult(res, intent);
         } catch (RemoteException e) {
         }
diff --git a/core/java/android/text/method/InsertModeTransformationMethod.java b/core/java/android/text/method/InsertModeTransformationMethod.java
index fbdaa3d..0c933d9 100644
--- a/core/java/android/text/method/InsertModeTransformationMethod.java
+++ b/core/java/android/text/method/InsertModeTransformationMethod.java
@@ -80,12 +80,34 @@
      */
     public InsertModeTransformationMethod(@IntRange(from = 0) int offset, boolean singleLine,
             @NonNull TransformationMethod oldTransformationMethod) {
-        mStart = offset;
-        mEnd = offset;
+        this(offset, offset, singleLine, oldTransformationMethod);
+    }
+
+    private InsertModeTransformationMethod(int start, int end, boolean singleLine,
+            @NonNull TransformationMethod oldTransformationMethod) {
+        mStart = start;
+        mEnd = end;
         mSingleLine = singleLine;
         mOldTransformationMethod = oldTransformationMethod;
     }
 
+    /**
+     * Create a new {@code InsertModeTransformation} with the given new inner
+     * {@code oldTransformationMethod} and the {@code singleLine} value. The returned
+     * {@link InsertModeTransformationMethod} will keep the highlight range.
+     *
+     * @param oldTransformationMethod the updated inner transformation method at the
+     * {@link android.widget.TextView}.
+     * @param singleLine the updated singleLine value.
+     * @return the new {@link InsertModeTransformationMethod} with the updated
+     * {@code oldTransformationMethod} and {@code singleLine} value.
+     */
+    public InsertModeTransformationMethod update(TransformationMethod oldTransformationMethod,
+            boolean singleLine) {
+        return new InsertModeTransformationMethod(mStart, mEnd, singleLine,
+                oldTransformationMethod);
+    }
+
     public TransformationMethod getOldTransformationMethod() {
         return mOldTransformationMethod;
     }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 4fcb137..e7cefd6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -108,6 +108,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
@@ -1105,6 +1106,26 @@
     public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
 
     /**
+     * Whether the device supports the WindowManager Extensions.
+     * OEMs can enable this by having their device config to inherit window_extensions.mk, such as:
+     * <pre>
+     * $(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)
+     * </pre>
+     * @hide
+     */
+    boolean WINDOW_EXTENSIONS_ENABLED =
+            SystemProperties.getBoolean("persist.wm.extensions.enabled", false);
+
+    /**
+     * @see #WINDOW_EXTENSIONS_ENABLED
+     * @hide
+     */
+    @TestApi
+    static boolean hasWindowExtensionsEnabled() {
+        return WINDOW_EXTENSIONS_ENABLED;
+    }
+
+    /**
      * Application-level
      * {@link android.content.pm.PackageManager.Property PackageManager.Property}
      * tag that specifies whether OEMs are permitted to provide activity
@@ -2785,10 +2806,10 @@
         /**
          * Never animate position changes of the window.
          *
+         * @see android.R.attr#Window_windowNoMoveAnimation
          * {@hide}
          */
         @UnsupportedAppUsage
-        @TestApi
         public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 0x00000040;
 
         /** Window flag: special flag to limit the size of the window to be
@@ -4211,6 +4232,31 @@
         }
 
         /**
+         * Set whether animations can be played for position changes on this window. If disabled,
+         * the window will move to its new position instantly without animating.
+         *
+         * @attr ref android.R.attr#Window_windowNoMoveAnimation
+         */
+        public void setCanPlayMoveAnimation(boolean enable) {
+            if (enable) {
+                privateFlags &= ~PRIVATE_FLAG_NO_MOVE_ANIMATION;
+            } else {
+                privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+            }
+        }
+
+        /**
+         * @return whether playing an animation during a position change is allowed on this window.
+         * This does not guarantee that an animation will be played in all such situations. For
+         * example, drag-resizing may move the window but not play an animation.
+         *
+         * @attr ref android.R.attr#Window_windowNoMoveAnimation
+         */
+        public boolean canPlayMoveAnimation() {
+            return (privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0;
+        }
+
+        /**
          * @return the {@link WindowInsets.Type}s that this window is avoiding overlapping.
          */
         public @InsetsType int getFitInsetsTypes() {
diff --git a/core/java/android/view/inputmethod/TextBoundsInfoResult.java b/core/java/android/view/inputmethod/TextBoundsInfoResult.java
index 62df17a..9dc11f0 100644
--- a/core/java/android/view/inputmethod/TextBoundsInfoResult.java
+++ b/core/java/android/view/inputmethod/TextBoundsInfoResult.java
@@ -17,7 +17,6 @@
 package android.view.inputmethod;
 
 import android.annotation.IntDef;
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.RectF;
 
@@ -98,7 +97,7 @@
      * is null.
      */
     public TextBoundsInfoResult(@ResultCode int resultCode,
-            @NonNull TextBoundsInfo textBoundsInfo) {
+            @Nullable TextBoundsInfo textBoundsInfo) {
         if (resultCode == CODE_SUCCESS && textBoundsInfo == null) {
             throw new IllegalStateException("TextBoundsInfo must be provided when the resultCode "
                     + "is CODE_SUCCESS.");
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 616957a..b89dd31 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -8072,7 +8072,7 @@
             final boolean isSingleLine = mTextView.isSingleLine();
             mInsertModeTransformationMethod = new InsertModeTransformationMethod(offset,
                     isSingleLine, oldTransformationMethod);
-            mTextView.setTransformationMethod(mInsertModeTransformationMethod);
+            mTextView.setTransformationMethodInternal(mInsertModeTransformationMethod);
             Selection.setSelection((Spannable) mTextView.getText(), offset);
 
             mIsInsertModeActive = true;
@@ -8083,11 +8083,6 @@
             if (!mIsInsertModeActive) return;
             if (mInsertModeTransformationMethod == null
                     || mInsertModeTransformationMethod != mTextView.getTransformationMethod()) {
-                // If mInsertionModeTransformationMethod doesn't match the one on TextView,
-                // something else have changed the TextView's TransformationMethod while the
-                // insertion mode is active. We don't need to restore the oldTransformationMethod.
-                // TODO(265871733): support the case where setTransformationMethod is called in
-                // the insert mode.
                 mIsInsertModeActive = false;
                 return;
             }
@@ -8097,7 +8092,7 @@
             final int selectionEnd = mTextView.getSelectionEnd();
             final TransformationMethod oldTransformationMethod =
                     mInsertModeTransformationMethod.getOldTransformationMethod();
-            mTextView.setTransformationMethod(oldTransformationMethod);
+            mTextView.setTransformationMethodInternal(oldTransformationMethod);
             Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd);
             mIsInsertModeActive = false;
         }
@@ -8118,6 +8113,25 @@
                 layout.getSelection(highlightStart, highlightEnd, consumer);
             }
         }
+
+        /**
+         * Notify the {@link InsertModeController} before the TextView's
+         * {@link TransformationMethod} is updated. If it's not in the insert mode,
+         * the given method is directly returned. Otherwise, it will wrap the given transformation
+         * method with an {@link InsertModeTransformationMethod} and then return.
+         *
+         * @param oldTransformationMethod the new {@link TransformationMethod} to be set on the
+         *                             TextView.
+         * @return the updated {@link TransformationMethod} to be set on the Textview.
+         */
+        TransformationMethod updateTransformationMethod(
+                TransformationMethod oldTransformationMethod) {
+            if (!mIsInsertModeActive) return oldTransformationMethod;
+
+            mInsertModeTransformationMethod = mInsertModeTransformationMethod.update(
+                    oldTransformationMethod, mTextView.isSingleLine());
+            return mInsertModeTransformationMethod;
+        }
     }
 
     boolean enterInsertMode(int offset) {
@@ -8134,6 +8148,26 @@
     }
 
     /**
+     * Called by the {@link TextView} when the {@link TransformationMethod} is updated.
+     *
+     * @param method the {@link TransformationMethod} to be set on the TextView.
+     */
+    void setTransformationMethod(TransformationMethod method) {
+        if (mInsertModeController == null || !mInsertModeController.mIsInsertModeActive) {
+            mTextView.setTransformationMethodInternal(method);
+            return;
+        }
+
+        // Changing TransformationMethod will reset selection range to [0, 0), we need to
+        // manually restore the old selection range.
+        final int selectionStart = mTextView.getSelectionStart();
+        final int selectionEnd = mTextView.getSelectionEnd();
+        method = mInsertModeController.updateTransformationMethod(method);
+        mTextView.setTransformationMethodInternal(method);
+        Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd);
+    }
+
+    /**
      * Initializes the nodeInfo with smart actions.
      */
     void onInitializeSmartActionsAccessibilityNodeInfo(AccessibilityNodeInfo nodeInfo) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 56bdea3..5de8079 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2769,6 +2769,14 @@
      * @attr ref android.R.styleable#TextView_singleLine
      */
     public final void setTransformationMethod(TransformationMethod method) {
+        if (mEditor != null) {
+            mEditor.setTransformationMethod(method);
+        } else {
+            setTransformationMethodInternal(method);
+        }
+    }
+
+    void setTransformationMethodInternal(@Nullable TransformationMethod method) {
         if (method == mTransformation) {
             // Avoid the setText() below if the transformation is
             // the same.
diff --git a/core/java/com/android/internal/app/ISoundTriggerService.aidl b/core/java/com/android/internal/app/ISoundTriggerService.aidl
index 3874de3..ab7f602 100644
--- a/core/java/com/android/internal/app/ISoundTriggerService.aidl
+++ b/core/java/com/android/internal/app/ISoundTriggerService.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.app;
 
 import android.media.permission.Identity;
+import android.hardware.soundtrigger.SoundTrigger;
 import com.android.internal.app.ISoundTriggerSession;
 
 /**
@@ -43,6 +44,7 @@
      * to clean-up whenever that happens.
      */
     ISoundTriggerSession attachAsOriginator(in Identity originatorIdentity,
+                                            in SoundTrigger.ModuleProperties moduleProperties,
                                             IBinder client);
 
     /**
@@ -64,5 +66,12 @@
      */
     ISoundTriggerSession attachAsMiddleman(in Identity middlemanIdentity,
                                            in Identity originatorIdentity,
+                                           in SoundTrigger.ModuleProperties moduleProperties,
                                            IBinder client);
+
+    /**
+     * Get available underlying SoundTrigger modules to attach to.
+     */
+    List<SoundTrigger.ModuleProperties> listModuleProperties(in Identity originatorIdentity);
+
 }
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 619b420..6b40d98 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -51,7 +51,7 @@
     int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
             String attributionTag);
     int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
-            String attributionTag);
+            String attributionTag, in Bundle bundle);
     void setKeepAwake(IBinder token, boolean keepAwake);
     void closeSystemDialogs(IBinder token);
     void finish(IBinder token);
@@ -243,7 +243,14 @@
      */
     IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator(
             in Identity originatorIdentity,
-            IBinder client);
+            IBinder client,
+            in SoundTrigger.ModuleProperties moduleProperties);
+
+    /**
+     * Lists properties of SoundTrigger modules that can be attached to by
+     * @{link createSoundTriggerSessionAsOriginator}.
+     */
+    List<SoundTrigger.ModuleProperties> listModuleProperties(in Identity originatorIdentity);
 
     /**
      * Set configuration and pass read-only data to hotword detection service.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index a14e9a7..c41b822 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2548,6 +2548,11 @@
             }
             params.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
         }
+        if (a.getBoolean(
+                R.styleable.Window_windowNoMoveAnimation,
+                false)) {
+            params.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+        }
         final int sysUiVis = decor.getSystemUiVisibility();
         final int statusLightFlag = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
         final int statusFlag = a.getBoolean(R.styleable.Window_windowLightStatusBar, false)
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 826624a..b9e5366 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2221,6 +2221,10 @@
         <!-- Elevation to use for the window. -->
         <attr name="windowElevation" format="dimension" />
 
+        <!-- Flag indicating whether this window should skip movement animations.
+             See also {@link android.view.WindowManager.LayoutParams#setCanPlayMoveAnimation} -->
+        <attr name="windowNoMoveAnimation" format="boolean" />
+
         <!-- Whether to clip window content to the outline of the window background. -->
         <attr name="windowClipToOutline" format="boolean" />
 
@@ -9022,10 +9026,10 @@
              {@link android.os.Build.VERSION_CODES#N} and not used in previous versions. -->
         <attr name="supportsLocalInteraction" format="boolean" />
         <!-- The service that provides {@link android.service.voice.HotwordDetectionService}.
-             @hide @SystemApi -->
+             Expect a component name to be provided. @hide @SystemApi -->
         <attr name="hotwordDetectionService" format="string" />
         <!-- The service that provides {@link android.service.voice.VisualQueryDetectionService}.
-             @hide @SystemApi -->
+             Expect a component name to be provided. @hide @SystemApi -->
         <attr name="visualQueryDetectionService" format="string" />
 
     </declare-styleable>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index b23d5b4..ee02100 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -129,6 +129,7 @@
     <public name="searchResultHighlightColor" />
     <public name="focusedSearchResultHighlightColor" />
     <public name="stylusHandwritingSettingsActivity" />
+    <public name="windowNoMoveAnimation" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01cd0000">
diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
index 76e761a..90bb627 100644
--- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
@@ -35,6 +35,8 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.SystemUtil;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -96,6 +98,7 @@
         mContext.registerReceiverAsUser(receiver, UserHandle.of(mCrossUserId),
                 new IntentFilter(Intent.ACTION_USER_UNLOCKED), null, null);
         ActivityManager.getService().startUserInBackground(mCrossUserId);
+        SystemUtil.runShellCommand("am wait-for-broadcast-barrier");
 
         try {
             if (!latch.await(TIMEOUT_USER_UNLOCK_SEC, TimeUnit.SECONDS)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index c5e374a..19d8cfa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -499,6 +499,11 @@
                             break;
                         }
                     }
+                } else if (mSideStage.getChildCount() != 0) {
+                    // There are chances the entering app transition got canceled by performing
+                    // rotation transition. Checks if there is any child task existed in split
+                    // screen before fallback to cancel entering flow.
+                    openingToSide = true;
                 }
 
                 if (isEnteringSplit && !openingToSide) {
@@ -515,7 +520,7 @@
                     }
                 }
 
-                if (!isEnteringSplit && openingToSide) {
+                if (!isEnteringSplit && apps != null) {
                     final WindowContainerTransaction evictWct = new WindowContainerTransaction();
                     prepareEvictNonOpeningChildTasks(position, apps, evictWct);
                     mSyncQueue.queue(evictWct);
@@ -598,6 +603,11 @@
                             break;
                         }
                     }
+                } else if (mSideStage.getChildCount() != 0) {
+                    // There are chances the entering app transition got canceled by performing
+                    // rotation transition. Checks if there is any child task existed in split
+                    // screen before fallback to cancel entering flow.
+                    openingToSide = true;
                 }
 
                 if (isEnteringSplit && !openingToSide && apps != null) {
@@ -624,7 +634,7 @@
                 }
 
 
-                if (!isEnteringSplit && openingToSide) {
+                if (!isEnteringSplit && apps != null) {
                     final WindowContainerTransaction evictWct = new WindowContainerTransaction();
                     prepareEvictNonOpeningChildTasks(position, apps, evictWct);
                     mSyncQueue.queue(evictWct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 3d1edc9..0a9c331 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -216,6 +216,7 @@
 
     @Override
     public void close() {
+        mInputEventReceiver.dispose();
         mInputChannel.dispose();
         try {
             mWindowSession.remove(mFakeWindow);
diff --git a/libs/androidfw/ApkParsing.cpp b/libs/androidfw/ApkParsing.cpp
index cf4fbb9..32d2c5b 100644
--- a/libs/androidfw/ApkParsing.cpp
+++ b/libs/androidfw/ApkParsing.cpp
@@ -56,10 +56,8 @@
         return nullptr;
     }
 
-    // Make sure there aren't subdirectories
-    const char* abiOffset = fileName + APK_LIB_LEN;
-    const size_t abiSize = lastSlash - abiOffset;
-    if (memchr(abiOffset, '/', abiSize)) {
+    // Make sure there aren't subdirectories by checking if the next / after lib/ is the last slash
+    if (memchr(fileName + APK_LIB_LEN, '/', fileNameLen - APK_LIB_LEN) != lastSlash) {
         return nullptr;
     }
 
diff --git a/libs/androidfw/tests/ApkParsing_test.cpp b/libs/androidfw/tests/ApkParsing_test.cpp
index 4aab0a1..62e88c6 100644
--- a/libs/androidfw/tests/ApkParsing_test.cpp
+++ b/libs/androidfw/tests/ApkParsing_test.cpp
@@ -68,4 +68,10 @@
   auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
   ASSERT_THAT(lastSlash, IsNull());
 }
+
+TEST(ApkParsingTest, InvalidFileAtRoot) {
+  const char* path = "lib/library.so";
+  auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+  ASSERT_THAT(lastSlash, IsNull());
+}
 }
\ No newline at end of file
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index d63b3e7..bcbe706 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -73,7 +73,6 @@
     target: {
         android: {
             include_dirs: [
-                "external/skia/src/effects",
                 "external/skia/src/image",
                 "external/skia/src/utils",
                 "external/skia/src/gpu",
@@ -386,9 +385,6 @@
         "external/skia/include/private",
         "external/skia/src/codec",
         "external/skia/src/core",
-        "external/skia/src/effects",
-        "external/skia/src/image",
-        "external/skia/src/images",
     ],
 
     shared_libs: [
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 6c070fe..8874ef1 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -2,9 +2,7 @@
 #define LOG_TAG "YuvToJpegEncoder"
 
 #include "CreateJavaOutputStreamAdaptor.h"
-#include "SkJPEGWriteUtility.h"
 #include "SkStream.h"
-#include "SkTypes.h"
 #include "YuvToJpegEncoder.h"
 #include <ui/PixelFormat.h>
 #include <hardware/hardware.h>
@@ -13,6 +11,15 @@
 
 #include <csetjmp>
 
+extern "C" {
+    // We need to include stdio.h before jpeg because jpeg does not include it, but uses FILE
+    // See https://github.com/libjpeg-turbo/libjpeg-turbo/issues/17
+    #include <stdio.h>
+    #include "jpeglib.h"
+    #include "jerror.h"
+    #include "jmorecfg.h"
+}
+
 YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) {
     // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported
     // for now.
@@ -39,11 +46,64 @@
     longjmp(err->jmp, 1);
 }
 
+/*
+ * Destination struct for directing decompressed pixels to a SkStream.
+ */
+static constexpr size_t kMgrBufferSize = 1024;
+struct skstream_destination_mgr : jpeg_destination_mgr {
+    skstream_destination_mgr(SkWStream* stream);
+
+    SkWStream* const fStream;
+
+    uint8_t fBuffer[kMgrBufferSize];
+};
+
+static void sk_init_destination(j_compress_ptr cinfo) {
+    skstream_destination_mgr* dest = (skstream_destination_mgr*)cinfo->dest;
+
+    dest->next_output_byte = dest->fBuffer;
+    dest->free_in_buffer = kMgrBufferSize;
+}
+
+static boolean sk_empty_output_buffer(j_compress_ptr cinfo) {
+    skstream_destination_mgr* dest = (skstream_destination_mgr*)cinfo->dest;
+
+    if (!dest->fStream->write(dest->fBuffer, kMgrBufferSize)) {
+        ERREXIT(cinfo, JERR_FILE_WRITE);
+        return FALSE;
+    }
+
+    dest->next_output_byte = dest->fBuffer;
+    dest->free_in_buffer = kMgrBufferSize;
+    return TRUE;
+}
+
+static void sk_term_destination(j_compress_ptr cinfo) {
+    skstream_destination_mgr* dest = (skstream_destination_mgr*)cinfo->dest;
+
+    size_t size = kMgrBufferSize - dest->free_in_buffer;
+    if (size > 0) {
+        if (!dest->fStream->write(dest->fBuffer, size)) {
+            ERREXIT(cinfo, JERR_FILE_WRITE);
+            return;
+        }
+    }
+
+    dest->fStream->flush();
+}
+
+skstream_destination_mgr::skstream_destination_mgr(SkWStream* stream)
+        : fStream(stream) {
+    this->init_destination = sk_init_destination;
+    this->empty_output_buffer = sk_empty_output_buffer;
+    this->term_destination = sk_term_destination;
+}
+
 bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width,
         int height, int* offsets, int jpegQuality) {
-    jpeg_compress_struct    cinfo;
-    ErrorMgr                err;
-    skjpeg_destination_mgr  sk_wstream(stream);
+    jpeg_compress_struct      cinfo;
+    ErrorMgr                  err;
+    skstream_destination_mgr  sk_wstream(stream);
 
     cinfo.err = jpeg_std_error(&err.pub);
     err.pub.error_exit = error_exit;
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index f739365..7eb0c76 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -94,5 +94,5 @@
     void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId,
             String sessionId, int volume);
     void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId);
-    void showMediaOutputSwitcher(String packageName);
+    boolean showMediaOutputSwitcher(String packageName);
 }
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 24ec227..c620229 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -461,16 +461,30 @@
     }
 
     /**
-     * Shows the system UI output switcher.
+     * Shows the system output switcher dialog.
+     *
+     * <p>Should only be called when the context of MediaRouter2 is in the foreground and visible on
+     * the screen.
+     *
+     * <p>The appearance and precise behaviour of the system output switcher dialog may vary across
+     * different devices, OS versions, and form factors, but the basic functionality stays the same.
+     *
+     * <p>See <a
+     * href="https://developer.android.com/guide/topics/media/media-routing#output-switcher">Output
+     * Switcher documentation</a> for more details.
+     *
+     * @return {@code true} if the output switcher dialog is being shown, or {@code false} if the
+     * call is ignored because the app is in the background.
      */
-    public void showSystemOutputSwitcher() {
+    public boolean showSystemOutputSwitcher() {
         synchronized (mLock) {
             try {
-                mMediaRouterService.showMediaOutputSwitcher(mPackageName);
+                return mMediaRouterService.showMediaOutputSwitcher(mPackageName);
             } catch (RemoteException ex) {
                 ex.rethrowFromSystemServer();
             }
         }
+        return false;
     }
 
     /**
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index c473a01..0e9ef4c 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -32,6 +32,7 @@
 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
+import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
 import android.hardware.soundtrigger.SoundTrigger.SoundModel;
 import android.media.permission.ClearCallingIdentityContext;
@@ -52,6 +53,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
 
@@ -92,8 +94,16 @@
             originatorIdentity.packageName = ActivityThread.currentOpPackageName();
 
             try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-                mSoundTriggerSession = soundTriggerService.attachAsOriginator(originatorIdentity,
-                        mBinderToken);
+                List<ModuleProperties> modulePropertiesList = soundTriggerService
+                        .listModuleProperties(originatorIdentity);
+                if (!modulePropertiesList.isEmpty()) {
+                    mSoundTriggerSession = soundTriggerService.attachAsOriginator(
+                                                originatorIdentity,
+                                                modulePropertiesList.get(0),
+                                                mBinderToken);
+                } else {
+                    mSoundTriggerSession = null;
+                }
             }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
@@ -110,6 +120,9 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @Deprecated
     public void updateModel(Model model) {
+        if (mSoundTriggerSession == null) {
+            throw new IllegalStateException("No underlying SoundTriggerModule available");
+        }
         try {
             mSoundTriggerSession.updateSoundModel(model.getGenericSoundModel());
         } catch (RemoteException e) {
@@ -128,6 +141,9 @@
     @Nullable
     @Deprecated
     public Model getModel(UUID soundModelId) {
+        if (mSoundTriggerSession == null) {
+            throw new IllegalStateException("No underlying SoundTriggerModule available");
+        }
         try {
             GenericSoundModel model =
                     mSoundTriggerSession.getSoundModel(new ParcelUuid(soundModelId));
@@ -149,6 +165,10 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @Deprecated
     public void deleteModel(UUID soundModelId) {
+        if (mSoundTriggerSession == null) {
+            throw new IllegalStateException("No underlying SoundTriggerModule available");
+        }
+
         try {
             mSoundTriggerSession.deleteSoundModel(new ParcelUuid(soundModelId));
         } catch (RemoteException e) {
@@ -176,7 +196,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     public SoundTriggerDetector createSoundTriggerDetector(UUID soundModelId,
             @NonNull SoundTriggerDetector.Callback callback, @Nullable Handler handler) {
-        if (soundModelId == null) {
+        if (soundModelId == null || mSoundTriggerSession == null) {
             return null;
         }
 
@@ -342,7 +362,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
     public int loadSoundModel(SoundModel soundModel) {
-        if (soundModel == null) {
+        if (soundModel == null || mSoundTriggerSession == null) {
             return STATUS_ERROR;
         }
 
@@ -389,7 +409,9 @@
         Preconditions.checkNotNull(soundModelId);
         Preconditions.checkNotNull(detectionService);
         Preconditions.checkNotNull(config);
-
+        if (mSoundTriggerSession == null) {
+            return STATUS_ERROR;
+        }
         try {
             return mSoundTriggerSession.startRecognitionForService(new ParcelUuid(soundModelId),
                 params, detectionService, config);
@@ -405,7 +427,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public int stopRecognition(UUID soundModelId) {
-        if (soundModelId == null) {
+        if (soundModelId == null || mSoundTriggerSession == null) {
             return STATUS_ERROR;
         }
         try {
@@ -422,7 +444,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public int unloadSoundModel(UUID soundModelId) {
-        if (soundModelId == null) {
+        if (soundModelId == null || mSoundTriggerSession == null) {
             return STATUS_ERROR;
         }
         try {
@@ -440,7 +462,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
     public boolean isRecognitionActive(UUID soundModelId) {
-        if (soundModelId == null) {
+        if (soundModelId == null || mSoundTriggerSession == null) {
             return false;
         }
         try {
@@ -475,7 +497,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
     public int getModelState(UUID soundModelId) {
-        if (soundModelId == null) {
+        if (soundModelId == null || mSoundTriggerSession == null) {
             return STATUS_ERROR;
         }
         try {
@@ -492,8 +514,10 @@
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @Nullable
-    public SoundTrigger.ModuleProperties getModuleProperties() {
-
+    public ModuleProperties getModuleProperties() {
+        if (mSoundTriggerSession == null) {
+            return null;
+        }
         try {
             return mSoundTriggerSession.getModuleProperties();
         } catch (RemoteException e) {
@@ -520,6 +544,10 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     public int setParameter(@Nullable UUID soundModelId,
             @ModelParams int modelParam, int value) {
+        if (mSoundTriggerSession == null) {
+            return SoundTrigger.STATUS_INVALID_OPERATION;
+        }
+
         try {
             return mSoundTriggerSession.setParameter(new ParcelUuid(soundModelId), modelParam,
                     value);
@@ -543,6 +571,10 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     public int getParameter(@NonNull UUID soundModelId,
             @ModelParams int modelParam) {
+        if (mSoundTriggerSession == null) {
+            throw new IllegalArgumentException("Sound model is not loaded: "
+                            + soundModelId.toString());
+        }
         try {
             return mSoundTriggerSession.getParameter(new ParcelUuid(soundModelId), modelParam);
         } catch (RemoteException e) {
@@ -563,6 +595,9 @@
     @Nullable
     public ModelParamRange queryParameter(@Nullable UUID soundModelId,
             @ModelParams int modelParam) {
+        if (mSoundTriggerSession == null) {
+            return null;
+        }
         try {
             return mSoundTriggerSession.queryParameter(new ParcelUuid(soundModelId), modelParam);
         } catch (RemoteException e) {
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
index ba30e79..afc2bb1 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -190,7 +190,9 @@
                 break;
             }
             case DO_SEND_TIME_SHIFT_MODE: {
-                mSessionImpl.sendTimeShiftMode((Integer) msg.obj);
+                SomeArgs args = (SomeArgs) msg.obj;
+                mSessionImpl.sendTimeShiftMode(args.argi1);
+                args.recycle();
                 break;
             }
             case DO_SEND_AVAILABLE_SPEEDS: {
@@ -445,8 +447,7 @@
 
     @Override
     public void sendTimeShiftMode(int mode) {
-        mCaller.executeOrSendMessage(
-                mCaller.obtainMessageI(DO_SEND_TIME_SHIFT_MODE, mode));
+        mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_SEND_TIME_SHIFT_MODE, mode));
     }
 
     @Override
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 41ac285..e886558 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -41,7 +41,6 @@
         </activity>
         <activity android:label="Camera2CtsActivity"
              android:name="Camera2SurfaceViewActivity"
-             android:screenOrientation="landscape"
              android:configChanges="keyboardHidden|orientation|screenSize"
              android:showWhenLocked="true"
              android:turnScreenOn="true"
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 9e249c4..45c4f88 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -148,7 +148,7 @@
         <provider android:name=".wear.WearPackageIconProvider"
                   android:authorities="com.google.android.packageinstaller.wear.provider"
                   android:grantUriPermissions="true"
-                  android:exported="true" />
+                  android:exported="false" />
     </application>
 
 </manifest>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index c702f59..11a8956 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -22,7 +22,7 @@
 import com.android.settingslib.spa.framework.common.SpaEnvironment
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlterDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
 import com.android.settingslib.spa.gallery.home.HomePageProvider
 import com.android.settingslib.spa.gallery.itemList.ItemListPageProvider
 import com.android.settingslib.spa.gallery.itemList.ItemOperatePageProvider
@@ -80,7 +80,7 @@
                 ProgressBarPageProvider,
                 LoadingBarPageProvider,
                 ChartPageProvider,
-                AlterDialogPageProvider,
+                AlertDialogPageProvider,
                 ItemListPageProvider,
                 ItemOperatePageProvider,
                 OperateListPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
similarity index 89%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
index 063b61c..1545a3e 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
@@ -28,14 +28,14 @@
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 
-private const val TITLE = "AlterDialogPage"
+private const val TITLE = "AlertDialogPage"
 
-object AlterDialogPageProvider : SettingsPageProvider {
-    override val name = "AlterDialogPage"
+object AlertDialogPageProvider : SettingsPageProvider {
+    override val name = "AlertDialogPage"
     private val owner = createSettingsPage()
 
     override fun buildEntry(arguments: Bundle?): List<SettingsEntry> = listOf(
-        SettingsEntryBuilder.create("AlterDialog", owner).setUiLayoutFn {
+        SettingsEntryBuilder.create("AlertDialog", owner).setUiLayoutFn {
             val alertDialogPresenter = rememberAlertDialogPresenter(
                 confirmButton = AlertDialogButton("Ok"),
                 dismissButton = AlertDialogButton("Cancel"),
@@ -43,7 +43,7 @@
                 text = { Text("Text") },
             )
             Preference(object : PreferenceModel {
-                override val title = "Show AlterDialog"
+                override val title = "Show AlertDialog"
                 override val onClick = alertDialogPresenter::open
             })
         }.build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
index 0878fc0..c60ebfd 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
@@ -28,7 +28,7 @@
 import com.android.settingslib.spa.gallery.R
 import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlterDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
 import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
 import com.android.settingslib.spa.gallery.page.ArgumentPageModel
 import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
@@ -64,7 +64,7 @@
             ProgressBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             LoadingBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
-            AlterDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+            AlertDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
         )
     }
 
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index be49940..e57cf3b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -148,9 +148,8 @@
                 Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, PERCENTAGE_INTEGER_VALIDATOR);
         VALIDATORS.put(Global.BLUETOOTH_ON, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.CLOCKWORK_HOME_READY, ANY_STRING_VALIDATOR);
-        VALIDATORS.put(Global.ENABLE_TARE, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Global.ENABLE_TARE_ALARM_MANAGER, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Global.ENABLE_TARE_JOB_SCHEDULER, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.ENABLE_TARE,
+                new DiscreteValueValidator(new String[] {"0", "1", "2"}));
         VALIDATORS.put(Global.TARE_ALARM_MANAGER_CONSTANTS, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Global.TARE_JOB_SCHEDULER_CONSTANTS, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Global.PRIVATE_DNS_MODE, ANY_STRING_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index e825e0e..c0176d5 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -266,8 +266,6 @@
                     Settings.Global.ENABLE_EPHEMERAL_FEATURE,
                     Settings.Global.ENABLE_RESTRICTED_BUCKET,
                     Settings.Global.ENABLE_TARE,
-                    Settings.Global.ENABLE_TARE_ALARM_MANAGER,
-                    Settings.Global.ENABLE_TARE_JOB_SCHEDULER,
                     Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
                     Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
                     Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
diff --git a/packages/SystemUI/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md
index 79d5718..d662649 100644
--- a/packages/SystemUI/docs/device-entry/quickaffordance.md
+++ b/packages/SystemUI/docs/device-entry/quickaffordance.md
@@ -52,6 +52,11 @@
 * Unselect an already-selected quick affordance from a slot
 * Unselect all already-selected quick affordances from a slot
 
+## Device Policy
+Returning `DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL` or
+`DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL` from
+`DevicePolicyManager#getKeyguardDisabledFeatures` will disable the keyguard quick affordance feature on the device.
+
 ## Testing
 * Add a unit test for your implementation of `KeyguardQuickAffordanceConfig`
 * Manually verify that your implementation works in multi-user environments from both the main user and a secondary user
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index addae72..13e2bca 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -683,7 +683,10 @@
     public void onTrustManagedChanged(boolean managed, int userId) {
         Assert.isMainThread();
         mUserTrustIsManaged.put(userId, managed);
-        mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
+        boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userId);
+        mLogger.logTrustUsuallyManagedUpdated(userId, mUserTrustIsUsuallyManaged.get(userId),
+                trustUsuallyManaged, "onTrustManagedChanged");
+        mUserTrustIsUsuallyManaged.put(userId, trustUsuallyManaged);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -2350,8 +2353,12 @@
         updateSecondaryLockscreenRequirement(user);
         List<UserInfo> allUsers = mUserManager.getUsers();
         for (UserInfo userInfo : allUsers) {
+            boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userInfo.id);
+            mLogger.logTrustUsuallyManagedUpdated(userInfo.id,
+                    mUserTrustIsUsuallyManaged.get(userInfo.id),
+                    trustUsuallyManaged, "init from constructor");
             mUserTrustIsUsuallyManaged.put(userInfo.id,
-                    mTrustManager.isTrustUsuallyManaged(userInfo.id));
+                    trustUsuallyManaged);
         }
         updateAirplaneModeState();
 
@@ -2391,9 +2398,11 @@
     }
 
     private void updateFaceEnrolled(int userId) {
-        mIsFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
+        Boolean isFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
                 && mBiometricEnabledForUser.get(userId)
                 && mAuthController.isFaceAuthEnrolled(userId);
+        mIsFaceEnrolled = isFaceEnrolled;
+        mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
     }
 
     public boolean isFaceSupported() {
@@ -3052,9 +3061,13 @@
     @VisibleForTesting
     boolean isUnlockWithFingerprintPossible(int userId) {
         // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
-        mIsUnlockWithFingerprintPossible.put(userId, mFpm != null
+        boolean fpEnrolled = mFpm != null
                 && !mFingerprintSensorProperties.isEmpty()
-                && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId));
+                && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId);
+        mLogger.logFpEnrolledUpdated(userId,
+                mIsUnlockWithFingerprintPossible.getOrDefault(userId, false),
+                fpEnrolled);
+        mIsUnlockWithFingerprintPossible.put(userId, fpEnrolled);
         return mIsUnlockWithFingerprintPossible.get(userId);
     }
 
@@ -3170,7 +3183,10 @@
     void handleUserSwitching(int userId, IRemoteCallback reply) {
         Assert.isMainThread();
         clearBiometricRecognized();
-        mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
+        boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userId);
+        mLogger.logTrustUsuallyManagedUpdated(userId, mUserTrustIsUsuallyManaged.get(userId),
+                trustUsuallyManaged, "userSwitching");
+        mUserTrustIsUsuallyManaged.put(userId, trustUsuallyManaged);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index c414c08..7707e1f 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -26,6 +26,7 @@
 import com.android.keyguard.KeyguardListenModel
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.keyguard.TrustGrantFlags
+import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogLevel
 import com.android.systemui.plugins.log.LogLevel.DEBUG
@@ -33,18 +34,15 @@
 import com.android.systemui.plugins.log.LogLevel.INFO
 import com.android.systemui.plugins.log.LogLevel.VERBOSE
 import com.android.systemui.plugins.log.LogLevel.WARNING
-import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
 import com.google.errorprone.annotations.CompileTimeConstant
 import javax.inject.Inject
 
 private const val TAG = "KeyguardUpdateMonitorLog"
 
-/**
- * Helper class for logging for [com.android.keyguard.KeyguardUpdateMonitor]
- */
-class KeyguardUpdateMonitorLogger @Inject constructor(
-        @KeyguardUpdateMonitorLog private val logBuffer: LogBuffer
-) {
+/** Helper class for logging for [com.android.keyguard.KeyguardUpdateMonitor] */
+class KeyguardUpdateMonitorLogger
+@Inject
+constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) {
     fun d(@CompileTimeConstant msg: String) = log(msg, DEBUG)
 
     fun e(@CompileTimeConstant msg: String) = log(msg, ERROR)
@@ -56,15 +54,16 @@
     fun log(@CompileTimeConstant msg: String, level: LogLevel) = logBuffer.log(TAG, level, msg)
 
     fun logActiveUnlockTriggered(reason: String?) {
-        logBuffer.log("ActiveUnlock", DEBUG,
-                { str1 = reason },
-                { "initiate active unlock triggerReason=$str1" })
+        logBuffer.log(
+            "ActiveUnlock",
+            DEBUG,
+            { str1 = reason },
+            { "initiate active unlock triggerReason=$str1" }
+        )
     }
 
     fun logAuthInterruptDetected(active: Boolean) {
-        logBuffer.log(TAG, DEBUG,
-                { bool1 = active },
-                { "onAuthInterruptDetected($bool1)" })
+        logBuffer.log(TAG, DEBUG, { bool1 = active }, { "onAuthInterruptDetected($bool1)" })
     }
 
     fun logBroadcastReceived(action: String?) {
@@ -72,9 +71,12 @@
     }
 
     fun logDeviceProvisionedState(deviceProvisioned: Boolean) {
-        logBuffer.log(TAG, DEBUG,
-                { bool1 = deviceProvisioned },
-                { "DEVICE_PROVISIONED state = $bool1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { bool1 = deviceProvisioned },
+            { "DEVICE_PROVISIONED state = $bool1" }
+        )
     }
 
     fun logException(ex: Exception, @CompileTimeConstant logMsg: String) {
@@ -82,46 +84,56 @@
     }
 
     fun logFaceAcquired(acquireInfo: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = acquireInfo },
-                { "Face acquired acquireInfo=$int1" })
+        logBuffer.log(TAG, DEBUG, { int1 = acquireInfo }, { "Face acquired acquireInfo=$int1" })
     }
 
     fun logFaceAuthDisabledForUser(userId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = userId },
-                { "Face authentication disabled by DPM for userId: $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = userId },
+            { "Face authentication disabled by DPM for userId: $int1" }
+        )
     }
     fun logFaceAuthError(msgId: Int, originalErrMsg: String) {
-        logBuffer.log(TAG, DEBUG, {
-                    str1 = originalErrMsg
-                    int1 = msgId
-                }, { "Face error received: $str1 msgId= $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = originalErrMsg
+                int1 = msgId
+            },
+            { "Face error received: $str1 msgId= $int1" }
+        )
     }
 
     fun logFaceAuthForWrongUser(authUserId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = authUserId },
-                { "Face authenticated for wrong user: $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = authUserId },
+            { "Face authenticated for wrong user: $int1" }
+        )
     }
 
     fun logFaceAuthHelpMsg(msgId: Int, helpMsg: String?) {
-        logBuffer.log(TAG, DEBUG, {
-                    int1 = msgId
-                    str1 = helpMsg
-                }, { "Face help received, msgId: $int1 msg: $str1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = msgId
+                str1 = helpMsg
+            },
+            { "Face help received, msgId: $int1 msg: $str1" }
+        )
     }
 
     fun logFaceAuthRequested(reason: String?) {
-        logBuffer.log(TAG, DEBUG, {
-            str1 = reason
-        }, { "requestFaceAuth() reason=$str1" })
+        logBuffer.log(TAG, DEBUG, { str1 = reason }, { "requestFaceAuth() reason=$str1" })
     }
 
     fun logFaceAuthSuccess(userId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = userId },
-                { "Face auth succeeded for user $int1" })
+        logBuffer.log(TAG, DEBUG, { int1 = userId }, { "Face auth succeeded for user $int1" })
     }
 
     fun logFaceLockoutReset(@LockoutMode mode: Int) {
@@ -133,21 +145,30 @@
     }
 
     fun logFaceUnlockPossible(isFaceUnlockPossible: Boolean) {
-        logBuffer.log(TAG, DEBUG,
-                { bool1 = isFaceUnlockPossible },
-                {"isUnlockWithFacePossible: $bool1"})
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { bool1 = isFaceUnlockPossible },
+            { "isUnlockWithFacePossible: $bool1" }
+        )
     }
 
     fun logFingerprintAuthForWrongUser(authUserId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = authUserId },
-                { "Fingerprint authenticated for wrong user: $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = authUserId },
+            { "Fingerprint authenticated for wrong user: $int1" }
+        )
     }
 
     fun logFingerprintDisabledForUser(userId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = userId },
-                { "Fingerprint disabled by DPM for userId: $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = userId },
+            { "Fingerprint disabled by DPM for userId: $int1" }
+        )
     }
 
     fun logFingerprintLockoutReset(@LockoutMode mode: Int) {
@@ -155,42 +176,63 @@
     }
 
     fun logFingerprintRunningState(fingerprintRunningState: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = fingerprintRunningState },
-                { "fingerprintRunningState: $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = fingerprintRunningState },
+            { "fingerprintRunningState: $int1" }
+        )
     }
 
     fun logFingerprintSuccess(userId: Int, isStrongBiometric: Boolean) {
-        logBuffer.log(TAG, DEBUG, {
-            int1 = userId
-            bool1 = isStrongBiometric
-        }, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"})
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = userId
+                bool1 = isStrongBiometric
+            },
+            { "Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1" }
+        )
     }
 
     fun logFingerprintError(msgId: Int, originalErrMsg: String) {
-        logBuffer.log(TAG, DEBUG, {
-            str1 = originalErrMsg
-            int1 = msgId
-        }, { "Fingerprint error received: $str1 msgId= $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = originalErrMsg
+                int1 = msgId
+            },
+            { "Fingerprint error received: $str1 msgId= $int1" }
+        )
     }
 
     fun logInvalidSubId(subId: Int) {
-        logBuffer.log(TAG, INFO,
-                { int1 = subId },
-                { "Previously active sub id $int1 is now invalid, will remove" })
+        logBuffer.log(
+            TAG,
+            INFO,
+            { int1 = subId },
+            { "Previously active sub id $int1 is now invalid, will remove" }
+        )
     }
 
     fun logPrimaryKeyguardBouncerChanged(
-            primaryBouncerIsOrWillBeShowing: Boolean,
-            primaryBouncerFullyShown: Boolean
+        primaryBouncerIsOrWillBeShowing: Boolean,
+        primaryBouncerFullyShown: Boolean
     ) {
-        logBuffer.log(TAG, DEBUG, {
-            bool1 = primaryBouncerIsOrWillBeShowing
-            bool2 = primaryBouncerFullyShown
-        }, {
-            "handlePrimaryBouncerChanged " +
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = primaryBouncerIsOrWillBeShowing
+                bool2 = primaryBouncerFullyShown
+            },
+            {
+                "handlePrimaryBouncerChanged " +
                     "primaryBouncerIsOrWillBeShowing=$bool1 primaryBouncerFullyShown=$bool2"
-        })
+            }
+        )
     }
 
     fun logKeyguardListenerModel(model: KeyguardListenModel) {
@@ -198,98 +240,134 @@
     }
 
     fun logKeyguardShowingChanged(showing: Boolean, occluded: Boolean, visible: Boolean) {
-        logBuffer.log(TAG, DEBUG, {
-            bool1 = showing
-            bool2 = occluded
-            bool3 = visible
-        }, {
-            "keyguardShowingChanged(showing=$bool1 occluded=$bool2 visible=$bool3)"
-        })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = showing
+                bool2 = occluded
+                bool3 = visible
+            },
+            { "keyguardShowingChanged(showing=$bool1 occluded=$bool2 visible=$bool3)" }
+        )
     }
 
     fun logMissingSupervisorAppError(userId: Int) {
-        logBuffer.log(TAG, ERROR,
-                { int1 = userId },
-                { "No Profile Owner or Device Owner supervision app found for User $int1" })
+        logBuffer.log(
+            TAG,
+            ERROR,
+            { int1 = userId },
+            { "No Profile Owner or Device Owner supervision app found for User $int1" }
+        )
     }
 
     fun logPhoneStateChanged(newState: String?) {
-        logBuffer.log(TAG, DEBUG,
-                { str1 = newState },
-                { "handlePhoneStateChanged($str1)" })
+        logBuffer.log(TAG, DEBUG, { str1 = newState }, { "handlePhoneStateChanged($str1)" })
     }
 
     fun logRegisterCallback(callback: KeyguardUpdateMonitorCallback?) {
-        logBuffer.log(TAG, VERBOSE,
-                { str1 = "$callback" },
-                { "*** register callback for $str1" })
+        logBuffer.log(TAG, VERBOSE, { str1 = "$callback" }, { "*** register callback for $str1" })
     }
 
     fun logRetryingAfterFaceHwUnavailable(retryCount: Int) {
-        logBuffer.log(TAG, WARNING,
-                { int1 = retryCount },
-                { "Retrying face after HW unavailable, attempt $int1" })
+        logBuffer.log(
+            TAG,
+            WARNING,
+            { int1 = retryCount },
+            { "Retrying face after HW unavailable, attempt $int1" }
+        )
     }
 
     fun logRetryAfterFpErrorWithDelay(msgId: Int, errString: String?, delay: Int) {
-        logBuffer.log(TAG, DEBUG, {
-            int1 = msgId
-            int2 = delay
-            str1 = "$errString"
-        }, {
-            "Fingerprint scheduling retry auth after $int2 ms due to($int1) -> $str1"
-        })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = msgId
+                int2 = delay
+                str1 = "$errString"
+            },
+            { "Fingerprint scheduling retry auth after $int2 ms due to($int1) -> $str1" }
+        )
     }
 
     fun logRetryAfterFpHwUnavailable(retryCount: Int) {
-        logBuffer.log(TAG, WARNING,
-                { int1 = retryCount },
-                { "Retrying fingerprint attempt: $int1" })
+        logBuffer.log(
+            TAG,
+            WARNING,
+            { int1 = retryCount },
+            { "Retrying fingerprint attempt: $int1" }
+        )
     }
 
     fun logSendPrimaryBouncerChanged(
         primaryBouncerIsOrWillBeShowing: Boolean,
         primaryBouncerFullyShown: Boolean,
     ) {
-        logBuffer.log(TAG, DEBUG, {
-            bool1 = primaryBouncerIsOrWillBeShowing
-            bool2 = primaryBouncerFullyShown
-        }, {
-            "sendPrimaryBouncerChanged primaryBouncerIsOrWillBeShowing=$bool1 " +
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = primaryBouncerIsOrWillBeShowing
+                bool2 = primaryBouncerFullyShown
+            },
+            {
+                "sendPrimaryBouncerChanged primaryBouncerIsOrWillBeShowing=$bool1 " +
                     "primaryBouncerFullyShown=$bool2"
-        })
+            }
+        )
     }
 
     fun logServiceStateChange(subId: Int, serviceState: ServiceState?) {
-        logBuffer.log(TAG, DEBUG, {
-            int1 = subId
-            str1 = "$serviceState"
-        }, { "handleServiceStateChange(subId=$int1, serviceState=$str1)" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = subId
+                str1 = "$serviceState"
+            },
+            { "handleServiceStateChange(subId=$int1, serviceState=$str1)" }
+        )
     }
 
     fun logServiceStateIntent(action: String?, serviceState: ServiceState?, subId: Int) {
-        logBuffer.log(TAG, VERBOSE, {
-            str1 = action
-            str2 = "$serviceState"
-            int1 = subId
-        }, { "action $str1 serviceState=$str2 subId=$int1" })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                str1 = action
+                str2 = "$serviceState"
+                int1 = subId
+            },
+            { "action $str1 serviceState=$str2 subId=$int1" }
+        )
     }
 
     fun logSimState(subId: Int, slotId: Int, state: Int) {
-        logBuffer.log(TAG, DEBUG, {
-            int1 = subId
-            int2 = slotId
-            long1 = state.toLong()
-        }, { "handleSimStateChange(subId=$int1, slotId=$int2, state=$long1)" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = subId
+                int2 = slotId
+                long1 = state.toLong()
+            },
+            { "handleSimStateChange(subId=$int1, slotId=$int2, state=$long1)" }
+        )
     }
 
     fun logSimStateFromIntent(action: String?, extraSimState: String?, slotId: Int, subId: Int) {
-        logBuffer.log(TAG, VERBOSE, {
-            str1 = action
-            str2 = extraSimState
-            int1 = slotId
-            int2 = subId
-        }, { "action $str1 state: $str2 slotId: $int1 subid: $int2" })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                str1 = action
+                str2 = extraSimState
+                int1 = slotId
+                int2 = subId
+            },
+            { "action $str1 state: $str2 slotId: $int1 subid: $int2" }
+        )
     }
 
     fun logSimUnlocked(subId: Int) {
@@ -297,78 +375,98 @@
     }
 
     fun logStartedListeningForFace(faceRunningState: Int, faceAuthUiEvent: FaceAuthUiEvent) {
-        logBuffer.log(TAG, VERBOSE, {
-            int1 = faceRunningState
-            str1 = faceAuthUiEvent.reason
-            str2 = faceAuthUiEvent.extraInfoToString()
-        }, { "startListeningForFace(): $int1, reason: $str1 $str2" })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                int1 = faceRunningState
+                str1 = faceAuthUiEvent.reason
+                str2 = faceAuthUiEvent.extraInfoToString()
+            },
+            { "startListeningForFace(): $int1, reason: $str1 $str2" }
+        )
     }
 
     fun logStartedListeningForFaceFromWakeUp(faceRunningState: Int, @WakeReason pmWakeReason: Int) {
-        logBuffer.log(TAG, VERBOSE, {
-            int1 = faceRunningState
-            str1 = PowerManager.wakeReasonToString(pmWakeReason)
-        }, { "startListeningForFace(): $int1, reason: wakeUp-$str1" })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                int1 = faceRunningState
+                str1 = PowerManager.wakeReasonToString(pmWakeReason)
+            },
+            { "startListeningForFace(): $int1, reason: wakeUp-$str1" }
+        )
     }
 
     fun logStoppedListeningForFace(faceRunningState: Int, faceAuthReason: String) {
-        logBuffer.log(TAG, VERBOSE, {
-            int1 = faceRunningState
-            str1 = faceAuthReason
-        }, { "stopListeningForFace(): currentFaceRunningState: $int1, reason: $str1" })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                int1 = faceRunningState
+                str1 = faceAuthReason
+            },
+            { "stopListeningForFace(): currentFaceRunningState: $int1, reason: $str1" }
+        )
     }
 
     fun logSubInfo(subInfo: SubscriptionInfo?) {
-        logBuffer.log(TAG, VERBOSE,
-                { str1 = "$subInfo" },
-                { "SubInfo:$str1" })
+        logBuffer.log(TAG, VERBOSE, { str1 = "$subInfo" }, { "SubInfo:$str1" })
     }
 
     fun logTimeFormatChanged(newTimeFormat: String?) {
-        logBuffer.log(TAG, DEBUG,
-                { str1 = newTimeFormat },
-                { "handleTimeFormatUpdate timeFormat=$str1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { str1 = newTimeFormat },
+            { "handleTimeFormatUpdate timeFormat=$str1" }
+        )
     }
     fun logUdfpsPointerDown(sensorId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = sensorId },
-                { "onUdfpsPointerDown, sensorId: $int1" })
+        logBuffer.log(TAG, DEBUG, { int1 = sensorId }, { "onUdfpsPointerDown, sensorId: $int1" })
     }
 
     fun logUdfpsPointerUp(sensorId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = sensorId },
-                { "onUdfpsPointerUp, sensorId: $int1" })
+        logBuffer.log(TAG, DEBUG, { int1 = sensorId }, { "onUdfpsPointerUp, sensorId: $int1" })
     }
 
     fun logUnexpectedFaceCancellationSignalState(faceRunningState: Int, unlockPossible: Boolean) {
-        logBuffer.log(TAG, ERROR, {
-                    int1 = faceRunningState
-                    bool1 = unlockPossible
-                }, {
-                    "Cancellation signal is not null, high chance of bug in " +
-                            "face auth lifecycle management. " +
-                            "Face state: $int1, unlockPossible: $bool1"
-                })
+        logBuffer.log(
+            TAG,
+            ERROR,
+            {
+                int1 = faceRunningState
+                bool1 = unlockPossible
+            },
+            {
+                "Cancellation signal is not null, high chance of bug in " +
+                    "face auth lifecycle management. " +
+                    "Face state: $int1, unlockPossible: $bool1"
+            }
+        )
     }
 
     fun logUnexpectedFpCancellationSignalState(
         fingerprintRunningState: Int,
         unlockPossible: Boolean
     ) {
-        logBuffer.log(TAG, ERROR, {
-                    int1 = fingerprintRunningState
-                    bool1 = unlockPossible
-                }, {
-                    "Cancellation signal is not null, high chance of bug in " +
-                            "fp auth lifecycle management. FP state: $int1, unlockPossible: $bool1"
-                })
+        logBuffer.log(
+            TAG,
+            ERROR,
+            {
+                int1 = fingerprintRunningState
+                bool1 = unlockPossible
+            },
+            {
+                "Cancellation signal is not null, high chance of bug in " +
+                    "fp auth lifecycle management. FP state: $int1, unlockPossible: $bool1"
+            }
+        )
     }
 
     fun logUnregisterCallback(callback: KeyguardUpdateMonitorCallback?) {
-        logBuffer.log(TAG, VERBOSE,
-                { str1 = "$callback" },
-                { "*** unregister callback for $str1" })
+        logBuffer.log(TAG, VERBOSE, { str1 = "$callback" }, { "*** unregister callback for $str1" })
     }
 
     fun logUserRequestedUnlock(
@@ -376,75 +474,149 @@
         reason: String?,
         dismissKeyguard: Boolean
     ) {
-        logBuffer.log("ActiveUnlock", DEBUG, {
-                    str1 = requestOrigin?.name
-                    str2 = reason
-                    bool1 = dismissKeyguard
-                }, { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" })
+        logBuffer.log(
+            "ActiveUnlock",
+            DEBUG,
+            {
+                str1 = requestOrigin?.name
+                str2 = reason
+                bool1 = dismissKeyguard
+            },
+            { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" }
+        )
     }
 
     fun logTrustGrantedWithFlags(
-            flags: Int,
-            newlyUnlocked: Boolean,
-            userId: Int,
-            message: String?
+        flags: Int,
+        newlyUnlocked: Boolean,
+        userId: Int,
+        message: String?
     ) {
-        logBuffer.log(TAG, DEBUG, {
-            int1 = flags
-            bool1 = newlyUnlocked
-            int2 = userId
-            str1 = message
-        }, { "trustGrantedWithFlags[user=$int2] newlyUnlocked=$bool1 " +
-                "flags=${TrustGrantFlags(int1)} message=$str1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = flags
+                bool1 = newlyUnlocked
+                int2 = userId
+                str1 = message
+            },
+            {
+                "trustGrantedWithFlags[user=$int2] newlyUnlocked=$bool1 " +
+                    "flags=${TrustGrantFlags(int1)} message=$str1"
+            }
+        )
     }
 
-    fun logTrustChanged(
-            wasTrusted: Boolean,
-            isNowTrusted: Boolean,
-            userId: Int
-    ) {
-        logBuffer.log(TAG, DEBUG, {
-            bool1 = wasTrusted
-            bool2 = isNowTrusted
-            int1 = userId
-        }, { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" })
+    fun logTrustChanged(wasTrusted: Boolean, isNowTrusted: Boolean, userId: Int) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = wasTrusted
+                bool2 = isNowTrusted
+                int1 = userId
+            },
+            { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" }
+        )
     }
 
     fun logKeyguardStateUpdate(
-            secure: Boolean,
-            canDismissLockScreen: Boolean,
-            trusted: Boolean,
-            trustManaged: Boolean
-
+        secure: Boolean,
+        canDismissLockScreen: Boolean,
+        trusted: Boolean,
+        trustManaged: Boolean
     ) {
-        logBuffer.log("KeyguardState", DEBUG, {
-            bool1 = secure
-            bool2 = canDismissLockScreen
-            bool3 = trusted
-            bool4 = trustManaged
-        }, { "#update secure=$bool1 canDismissKeyguard=$bool2" +
-                " trusted=$bool3 trustManaged=$bool4" })
+        logBuffer.log(
+            "KeyguardState",
+            DEBUG,
+            {
+                bool1 = secure
+                bool2 = canDismissLockScreen
+                bool3 = trusted
+                bool4 = trustManaged
+            },
+            {
+                "#update secure=$bool1 canDismissKeyguard=$bool2" +
+                    " trusted=$bool3 trustManaged=$bool4"
+            }
+        )
     }
 
     fun logSkipUpdateFaceListeningOnWakeup(@WakeReason pmWakeReason: Int) {
-        logBuffer.log(TAG, VERBOSE, {
-            str1 = PowerManager.wakeReasonToString(pmWakeReason)
-        }, { "Skip updating face listening state on wakeup from $str1"})
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            { str1 = PowerManager.wakeReasonToString(pmWakeReason) },
+            { "Skip updating face listening state on wakeup from $str1" }
+        )
     }
 
     fun logTaskStackChangedForAssistant(assistantVisible: Boolean) {
-        logBuffer.log(TAG, VERBOSE, {
-            bool1 = assistantVisible
-        }, {
-            "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1"
-        })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            { bool1 = assistantVisible },
+            { "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1" }
+        )
     }
 
     fun logAssistantVisible(assistantVisible: Boolean) {
-        logBuffer.log(TAG, VERBOSE, {
-            bool1 = assistantVisible
-        }, {
-            "Updating mAssistantVisible to new value: $bool1"
-        })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            { bool1 = assistantVisible },
+            { "Updating mAssistantVisible to new value: $bool1" }
+        )
+    }
+
+    fun logFaceEnrolledUpdated(oldValue: Boolean, newValue: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = oldValue
+                bool2 = newValue
+            },
+            { "Face enrolled state changed: old: $bool1, new: $bool2" }
+        )
+    }
+
+    fun logFpEnrolledUpdated(userId: Int, oldValue: Boolean, newValue: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = userId
+                bool1 = oldValue
+                bool2 = newValue
+            },
+            { "Fp enrolled state changed for userId: $int1 old: $bool1, new: $bool2" }
+        )
+    }
+
+    fun logTrustUsuallyManagedUpdated(
+        userId: Int,
+        oldValue: Boolean,
+        newValue: Boolean,
+        context: String
+    ) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = userId
+                bool1 = oldValue
+                bool2 = newValue
+                str1 = context
+            },
+            {
+                "trustUsuallyManaged changed for " +
+                    "userId: $int1 " +
+                    "old: $bool1, " +
+                    "new: $bool2 " +
+                    "context: $str1"
+            }
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index d8d8c0e..3a3f9b4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -134,7 +134,8 @@
         super.onStop()
         mExitToDream = false
 
-        uiController.hide()
+        // parent is set in onStart, so the field is initialized when we get here
+        uiController.hide(parent)
         controlsSettingsDialogManager.closeDialog()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index c1cec9d..58673bb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -31,7 +31,13 @@
     }
 
     fun show(parent: ViewGroup, onDismiss: Runnable, activityContext: Context)
-    fun hide()
+
+    /**
+     * Hide the controls content if it's attached to this parent.
+     */
+    fun hide(parent: ViewGroup)
+
+    val isShowing: Boolean
 
     /**
      * Returns the preferred activity to start, depending on if the user has favorited any
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 58f4835..9405c60 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -168,6 +168,9 @@
     private lateinit var activityContext: Context
     private lateinit var listingCallback: ControlsListingController.ControlsListingCallback
 
+    override val isShowing: Boolean
+        get() = !hidden
+
     init {
         dumpManager.registerDumpable(javaClass.name, this)
     }
@@ -727,21 +730,27 @@
         controlActionCoordinator.closeDialogs()
     }
 
-    override fun hide() {
-        hidden = true
+    override fun hide(parent: ViewGroup) {
+        // We need to check for the parent because it's possible that  we have started showing in a
+        // different activity. In that case, make sure to only clear things associated with the
+        // passed parent
+        if (parent == this.parent) {
+            Log.d(ControlsUiController.TAG, "hide()")
+            hidden = true
 
-        closeDialogs(true)
-        controlsController.get().unsubscribe()
-        taskViewController?.dismiss()
-        taskViewController = null
+            closeDialogs(true)
+            controlsController.get().unsubscribe()
+            taskViewController?.dismiss()
+            taskViewController = null
 
+            controlsById.clear()
+            controlViewsById.clear()
+
+            controlsListingController.get().removeCallback(listingCallback)
+
+            if (!retainCache) RenderInfo.clearCache()
+        }
         parent.removeAllViews()
-        controlsById.clear()
-        controlViewsById.clear()
-
-        controlsListingController.get().removeCallback(listingCallback)
-
-        if (!retainCache) RenderInfo.clearCache()
     }
 
     override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index da59996..fa4caaf 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -59,7 +59,7 @@
         )
 
     // TODO(b/254512517): Tracking Bug
-    val FSI_REQUIRES_KEYGUARD = unreleasedFlag(110, "fsi_requires_keyguard", teamfood = true)
+    val FSI_REQUIRES_KEYGUARD = releasedFlag(110, "fsi_requires_keyguard")
 
     // TODO(b/259130119): Tracking Bug
     val FSI_ON_DND_UPDATE = unreleasedFlag(259130119, "fsi_on_dnd_update", teamfood = true)
@@ -187,7 +187,7 @@
 
     // TODO(b/262780002): Tracking Bug
     @JvmField
-    val REVAMPED_WALLPAPER_UI = unreleasedFlag(222, "revamped_wallpaper_ui", teamfood = false)
+    val REVAMPED_WALLPAPER_UI = unreleasedFlag(222, "revamped_wallpaper_ui", teamfood = true)
 
     /** A different path for unocclusion transitions back to keyguard */
     // TODO(b/262859270): Tracking Bug
@@ -217,6 +217,7 @@
         unreleasedFlag(
             228,
             "lock_screen_long_press_enabled",
+            teamfood = true,
         )
 
     // 300 - power menu
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index 680c504..27a5974 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -131,7 +131,7 @@
             throw UnsupportedOperationException()
         }
 
-        return insertSelection(values)
+        return runBlocking(mainDispatcher) { insertSelection(values) }
     }
 
     override fun query(
@@ -171,7 +171,7 @@
             throw UnsupportedOperationException()
         }
 
-        return deleteSelection(uri, selectionArgs)
+        return runBlocking(mainDispatcher) { deleteSelection(uri, selectionArgs) }
     }
 
     override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
@@ -189,7 +189,7 @@
         }
     }
 
-    private fun insertSelection(values: ContentValues?): Uri? {
+    private suspend fun insertSelection(values: ContentValues?): Uri? {
         if (values == null) {
             throw IllegalArgumentException("Cannot insert selection, no values passed in!")
         }
@@ -311,7 +311,7 @@
             }
     }
 
-    private fun querySlots(): Cursor {
+    private suspend fun querySlots(): Cursor {
         return MatrixCursor(
                 arrayOf(
                     Contract.LockScreenQuickAffordances.SlotTable.Columns.ID,
@@ -330,7 +330,7 @@
             }
     }
 
-    private fun queryFlags(): Cursor {
+    private suspend fun queryFlags(): Cursor {
         return MatrixCursor(
                 arrayOf(
                     Contract.FlagsTable.Columns.NAME,
@@ -353,7 +353,7 @@
             }
     }
 
-    private fun deleteSelection(
+    private suspend fun deleteSelection(
         uri: Uri,
         selectionArgs: Array<out String>?,
     ): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index fb0fb1f..dfbe1c2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -18,12 +18,14 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.app.AlertDialog
+import android.app.admin.DevicePolicyManager
 import android.content.Intent
 import android.util.Log
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
@@ -41,13 +43,17 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import dagger.Lazy
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardQuickAffordanceInteractor
 @Inject
@@ -61,6 +67,8 @@
     private val featureFlags: FeatureFlags,
     private val repository: Lazy<KeyguardQuickAffordanceRepository>,
     private val launchAnimator: DialogLaunchAnimator,
+    private val devicePolicyManager: DevicePolicyManager,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) {
     private val isUsingRepository: Boolean
         get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
@@ -74,9 +82,13 @@
         get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
 
     /** Returns an observable for the quick affordance at the given position. */
-    fun quickAffordance(
+    suspend fun quickAffordance(
         position: KeyguardQuickAffordancePosition
     ): Flow<KeyguardQuickAffordanceModel> {
+        if (isFeatureDisabledByDevicePolicy()) {
+            return flowOf(KeyguardQuickAffordanceModel.Hidden)
+        }
+
         return combine(
             quickAffordanceAlwaysVisible(position),
             keyguardInteractor.isDozing,
@@ -148,8 +160,11 @@
      *
      * @return `true` if the affordance was selected successfully; `false` otherwise.
      */
-    fun select(slotId: String, affordanceId: String): Boolean {
+    suspend fun select(slotId: String, affordanceId: String): Boolean {
         check(isUsingRepository)
+        if (isFeatureDisabledByDevicePolicy()) {
+            return false
+        }
 
         val slots = repository.get().getSlotPickerRepresentations()
         val slot = slots.find { it.id == slotId } ?: return false
@@ -187,8 +202,11 @@
      * @return `true` if the affordance was successfully removed; `false` otherwise (for example, if
      * the affordance was not on the slot to begin with).
      */
-    fun unselect(slotId: String, affordanceId: String?): Boolean {
+    suspend fun unselect(slotId: String, affordanceId: String?): Boolean {
         check(isUsingRepository)
+        if (isFeatureDisabledByDevicePolicy()) {
+            return false
+        }
 
         val slots = repository.get().getSlotPickerRepresentations()
         if (slots.find { it.id == slotId } == null) {
@@ -227,6 +245,10 @@
 
     /** Returns affordance IDs indexed by slot ID, for all known slots. */
     suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
+        if (isFeatureDisabledByDevicePolicy()) {
+            return emptyMap()
+        }
+
         val slots = repository.get().getSlotPickerRepresentations()
         val selections = repository.get().getCurrentSelections()
         val affordanceById =
@@ -351,13 +373,17 @@
         return repository.get().getAffordancePickerRepresentations()
     }
 
-    fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> {
+    suspend fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> {
         check(isUsingRepository)
 
+        if (isFeatureDisabledByDevicePolicy()) {
+            return emptyList()
+        }
+
         return repository.get().getSlotPickerRepresentations()
     }
 
-    fun getPickerFlags(): List<KeyguardPickerFlag> {
+    suspend fun getPickerFlags(): List<KeyguardPickerFlag> {
         return listOf(
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_REVAMPED_WALLPAPER_UI,
@@ -365,7 +391,9 @@
             ),
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
-                value = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
+                value =
+                    !isFeatureDisabledByDevicePolicy() &&
+                        featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
             ),
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
@@ -382,6 +410,17 @@
         )
     }
 
+    private suspend fun isFeatureDisabledByDevicePolicy(): Boolean {
+        val flags =
+            withContext(backgroundDispatcher) {
+                devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId)
+            }
+        val flagsToCheck =
+            DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL or
+                DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
+        return flagsToCheck and flags != 0
+    }
+
     companion object {
         private const val TAG = "KeyguardQuickAffordanceInteractor"
         private const val DELIMITER = "::"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7067c220..a0be151 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -25,8 +25,6 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -108,6 +106,11 @@
     private boolean mShouldMoveMediaOnExpansion = true;
     private boolean mUsingCombinedHeaders = false;
     private QSLogger mQsLogger;
+    /**
+     * Specifies if we can collapse to QQS in current state. In split shade that should be always
+     * false. It influences available accessibility actions.
+     */
+    private boolean mCanCollapse = true;
 
     public QSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -650,7 +653,9 @@
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
-        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+        if (mCanCollapse) {
+            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+        }
     }
 
     @Override
@@ -669,15 +674,11 @@
         mCollapseExpandAction = action;
     }
 
-    private class H extends Handler {
-        private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1;
-
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) {
-                announceForAccessibility((CharSequence) msg.obj);
-            }
-        }
+    /**
+     * Specifies if these expanded QS can collapse to QQS.
+     */
+    public void setCanCollapse(boolean canCollapse) {
+        mCanCollapse = canCollapse;
     }
 
     public interface QSTileLayout {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index cabe1da..01dbb18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -148,9 +148,10 @@
     }
 
     @Override
-    protected void onSplitShadeChanged() {
+    protected void onSplitShadeChanged(boolean shouldUseSplitNotificationShade) {
         ((PagedTileLayout) mView.getOrCreateTileLayout())
                 .forceTilesRedistribution("Split shade state changed");
+        mView.setCanCollapse(!shouldUseSplitNotificationShade);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index e85d0a3..bbdf6cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -104,14 +104,14 @@
                     switchTileLayoutIfNeeded();
                     onConfigurationChanged();
                     if (previousSplitShadeState != mShouldUseSplitNotificationShade) {
-                        onSplitShadeChanged();
+                        onSplitShadeChanged(mShouldUseSplitNotificationShade);
                     }
                 }
             };
 
     protected void onConfigurationChanged() { }
 
-    protected void onSplitShadeChanged() { }
+    protected void onSplitShadeChanged(boolean shouldUseSplitNotificationShade) { }
 
     private final Function1<Boolean, Unit> mMediaHostVisibilityListener = (visible) -> {
         if (mMediaVisibilityChangedListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 80f7c36..9d8ed46 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shade
 
 import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
 import android.annotation.IdRes
 import android.app.StatusBarManager
 import android.content.res.Configuration
@@ -145,6 +146,14 @@
             updateListeners()
         }
 
+    private var customizing = false
+        set(value) {
+            if (field != value) {
+                field = value
+                updateVisibility()
+            }
+        }
+
     /**
      * Whether the QQS/QS part of the shade is visible. This is particularly important in
      * Lockscreen, as the shade is visible but QS is not.
@@ -176,14 +185,9 @@
      */
     var shadeExpandedFraction = -1f
         set(value) {
-            if (field != value) {
-                val oldAlpha = header.alpha
+            if (qsVisible && field != value) {
                 header.alpha = ShadeInterpolation.getContentAlpha(value)
                 field = value
-                if ((oldAlpha == 0f && header.alpha > 0f) ||
-                        (oldAlpha > 0f && header.alpha == 0f)) {
-                    updateVisibility()
-                }
             }
         }
 
@@ -318,6 +322,7 @@
         dumpManager.registerDumpable(this)
         configurationController.addCallback(configurationControllerListener)
         demoModeController.addCallback(demoModeReceiver)
+        statusBarIconController.addIconGroup(iconManager)
     }
 
     override fun onViewDetached() {
@@ -325,6 +330,7 @@
         dumpManager.unregisterDumpable(this::class.java.simpleName)
         configurationController.removeCallback(configurationControllerListener)
         demoModeController.removeCallback(demoModeReceiver)
+        statusBarIconController.removeIconGroup(iconManager)
     }
 
     fun disable(state1: Int, state2: Int, animate: Boolean) {
@@ -339,31 +345,10 @@
                 .setDuration(duration)
                 .alpha(if (show) 0f else 1f)
                 .setInterpolator(if (show) Interpolators.ALPHA_OUT else Interpolators.ALPHA_IN)
-                .setUpdateListener {
-                    updateVisibility()
-                }
-                .setListener(endAnimationListener)
+                .setListener(CustomizerAnimationListener(show))
                 .start()
     }
 
-    private val endAnimationListener = object : Animator.AnimatorListener {
-        override fun onAnimationCancel(animation: Animator?) {
-            clearListeners()
-        }
-
-        override fun onAnimationEnd(animation: Animator?) {
-            clearListeners()
-        }
-
-        override fun onAnimationRepeat(animation: Animator?) {}
-
-        override fun onAnimationStart(animation: Animator?) {}
-
-        private fun clearListeners() {
-            header.animate().setListener(null).setUpdateListener(null)
-        }
-    }
-
     private fun loadConstraints() {
         if (header is MotionLayout) {
             // Use resources.getXml instead of passing the resource id due to bug b/205018300
@@ -453,7 +438,7 @@
     private fun updateVisibility() {
         val visibility = if (!largeScreenActive && !combinedHeaders || qsDisabled) {
             View.GONE
-        } else if (qsVisible && header.alpha > 0f) {
+        } else if (qsVisible && !customizing) {
             View.VISIBLE
         } else {
             View.INVISIBLE
@@ -502,10 +487,8 @@
         if (visible) {
             updateSingleCarrier(qsCarrierGroupController.isSingleCarrier)
             qsCarrierGroupController.setOnSingleCarrierChangedListener { updateSingleCarrier(it) }
-            statusBarIconController.addIconGroup(iconManager)
         } else {
             qsCarrierGroupController.setOnSingleCarrierChangedListener(null)
-            statusBarIconController.removeIconGroup(iconManager)
         }
     }
 
@@ -578,4 +561,23 @@
 
     @VisibleForTesting
     internal fun simulateViewDetached() = this.onViewDetached()
+
+    inner class CustomizerAnimationListener(
+            private val enteringCustomizing: Boolean,
+    ) : AnimatorListenerAdapter() {
+        override fun onAnimationEnd(animation: Animator?) {
+            super.onAnimationEnd(animation)
+            header.animate().setListener(null)
+            if (enteringCustomizing) {
+                customizing = true
+            }
+        }
+
+        override fun onAnimationStart(animation: Animator?) {
+            super.onAnimationStart(animation)
+            if (!enteringCustomizing) {
+                customizing = false
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index aafd7e6..5ed11c2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -4636,11 +4636,6 @@
 
             switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:
-                    if (mTracking) {
-                        // TODO(b/247126247) fix underlying issue. Should be ACTION_POINTER_DOWN.
-                        mShadeLog.d("Don't intercept down event while already tracking");
-                        return false;
-                    }
                     mCentralSurfaces.userActivity();
                     mAnimatingOnDown = mHeightAnimator != null && !mIsSpringBackAnimation;
                     mMinExpandHeight = 0.0f;
@@ -4728,11 +4723,6 @@
                             "onTouch: duplicate down event detected... ignoring");
                     return true;
                 }
-                if (mTracking) {
-                    // TODO(b/247126247) fix underlying issue. Should be ACTION_POINTER_DOWN.
-                    mShadeLog.d("Don't handle down event while already tracking");
-                    return true;
-                }
                 mLastTouchDownTime = event.getDownTime();
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 21f4cb5..49f17b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -88,6 +88,7 @@
         mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer);
         updateResources();
         updateText();
+        updateColors();
     }
 
     public void setFooterLabelTextAndIcon(@StringRes int text, @DrawableRes int icon) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index 85f9961..aa90e2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -29,6 +29,7 @@
 import android.util.AttributeSet
 import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewGroup
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
@@ -328,7 +329,7 @@
             )
             .isTrue()
 
-        underTest.hide()
+        underTest.hide(parent)
 
         clearInvocations(controlsListingController, taskViewFactory)
         controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(false)
@@ -387,6 +388,28 @@
         assertThat(underTest.resolveActivity()).isEqualTo(ControlsActivity::class.java)
     }
 
+    @Test
+    fun testRemoveViewsOnlyForParentPassedInHide() {
+        underTest.show(parent, {}, context)
+        parent.addView(View(context))
+
+        val mockParent: ViewGroup = mock()
+
+        underTest.hide(mockParent)
+
+        verify(mockParent).removeAllViews()
+        assertThat(parent.childCount).isGreaterThan(0)
+    }
+
+    @Test
+    fun testHideDifferentParentDoesntCancelListeners() {
+        underTest.show(parent, {}, context)
+        underTest.hide(mock())
+
+        verify(controlsController, never()).unsubscribe()
+        verify(controlsListingController, never()).removeCallback(any())
+    }
+
     private fun setUpPanel(panel: SelectedItem.PanelItem): ControlsServiceInfo {
         val activity = ComponentName("pkg", "activity")
         sharedPreferences
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 15a454b..a4e5bca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard
 
+import android.app.admin.DevicePolicyManager
 import android.content.ContentValues
 import android.content.pm.PackageManager
 import android.content.pm.ProviderInfo
@@ -61,7 +62,6 @@
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
@@ -90,6 +90,7 @@
     @Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: CustomizationProvider
     private lateinit var testScope: TestScope
@@ -102,7 +103,7 @@
         whenever(backgroundHandler.looper).thenReturn(TestableLooper.get(this).looper)
 
         underTest = CustomizationProvider()
-        val testDispatcher = StandardTestDispatcher()
+        val testDispatcher = UnconfinedTestDispatcher()
         testScope = TestScope(testDispatcher)
         val localUserSelectionManager =
             KeyguardQuickAffordanceLocalUserSelectionManager(
@@ -183,6 +184,8 @@
                 featureFlags = featureFlags,
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
+                devicePolicyManager = devicePolicyManager,
+                backgroundDispatcher = testDispatcher,
             )
         underTest.previewManager =
             KeyguardRemotePreviewManager(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 23e06ec..84ec125 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.app.admin.DevicePolicyManager
 import android.content.Intent
 import android.os.UserHandle
 import androidx.test.filters.SmallTest
@@ -54,7 +55,10 @@
 import com.android.systemui.util.settings.FakeSettings
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -70,6 +74,7 @@
 import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(Parameterized::class)
 class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
@@ -219,8 +224,10 @@
     @Mock private lateinit var expandable: Expandable
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: KeyguardQuickAffordanceInteractor
+    private lateinit var testScope: TestScope
 
     @JvmField @Parameter(0) var needStrongAuthAfterBoot: Boolean = false
     @JvmField @Parameter(1) var canShowWhileLocked: Boolean = false
@@ -292,6 +299,8 @@
                 set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
                 set(Flags.FACE_AUTH_REFACTOR, true)
             }
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
         underTest =
             KeyguardQuickAffordanceInteractor(
                 keyguardInteractor =
@@ -322,58 +331,61 @@
                 featureFlags = featureFlags,
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
+                devicePolicyManager = devicePolicyManager,
+                backgroundDispatcher = testDispatcher,
             )
     }
 
     @Test
-    fun onQuickAffordanceTriggered() = runBlockingTest {
-        setUpMocks(
-            needStrongAuthAfterBoot = needStrongAuthAfterBoot,
-            keyguardIsUnlocked = keyguardIsUnlocked,
-        )
+    fun onQuickAffordanceTriggered() =
+        testScope.runTest {
+            setUpMocks(
+                needStrongAuthAfterBoot = needStrongAuthAfterBoot,
+                keyguardIsUnlocked = keyguardIsUnlocked,
+            )
 
-        homeControls.setState(
-            lockScreenState =
-                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
-                    icon = DRAWABLE,
-                )
-        )
-        homeControls.onTriggeredResult =
+            homeControls.setState(
+                lockScreenState =
+                    KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                        icon = DRAWABLE,
+                    )
+            )
+            homeControls.onTriggeredResult =
+                if (startActivity) {
+                    KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
+                        intent = INTENT,
+                        canShowWhileLocked = canShowWhileLocked,
+                    )
+                } else {
+                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                }
+
+            underTest.onQuickAffordanceTriggered(
+                configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
+                expandable = expandable,
+            )
+
             if (startActivity) {
-                KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
-                    intent = INTENT,
-                    canShowWhileLocked = canShowWhileLocked,
-                )
+                if (needsToUnlockFirst) {
+                    verify(activityStarter)
+                        .postStartActivityDismissingKeyguard(
+                            any(),
+                            /* delay= */ eq(0),
+                            same(animationController),
+                        )
+                } else {
+                    verify(activityStarter)
+                        .startActivity(
+                            any(),
+                            /* dismissShade= */ eq(true),
+                            same(animationController),
+                            /* showOverLockscreenWhenLocked= */ eq(true),
+                        )
+                }
             } else {
-                KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                verifyZeroInteractions(activityStarter)
             }
-
-        underTest.onQuickAffordanceTriggered(
-            configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
-            expandable = expandable,
-        )
-
-        if (startActivity) {
-            if (needsToUnlockFirst) {
-                verify(activityStarter)
-                    .postStartActivityDismissingKeyguard(
-                        any(),
-                        /* delay= */ eq(0),
-                        same(animationController),
-                    )
-            } else {
-                verify(activityStarter)
-                    .startActivity(
-                        any(),
-                        /* dismissShade= */ eq(true),
-                        same(animationController),
-                        /* showOverLockscreenWhenLocked= */ eq(true),
-                    )
-            }
-        } else {
-            verifyZeroInteractions(activityStarter)
         }
-    }
 
     private fun setUpMocks(
         needStrongAuthAfterBoot: Boolean = true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 1b8c627..62c9e5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.app.admin.DevicePolicyManager
 import android.os.UserHandle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -78,6 +79,7 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: KeyguardQuickAffordanceInteractor
 
@@ -184,6 +186,8 @@
                 featureFlags = featureFlags,
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
+                devicePolicyManager = devicePolicyManager,
+                backgroundDispatcher = testDispatcher,
             )
     }
 
@@ -239,6 +243,44 @@
         }
 
     @Test
+    fun `quickAffordance - hidden when all features are disabled by device policy`() =
+        testScope.runTest {
+            whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
+            quickAccessWallet.setState(
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                    icon = ICON,
+                )
+            )
+
+            val collectedValue by
+                collectLastValue(
+                    underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
+                )
+
+            assertThat(collectedValue).isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java)
+        }
+
+    @Test
+    fun `quickAffordance - hidden when shortcuts feature is disabled by device policy`() =
+        testScope.runTest {
+            whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
+            quickAccessWallet.setState(
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                    icon = ICON,
+                )
+            )
+
+            val collectedValue by
+                collectLastValue(
+                    underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
+                )
+
+            assertThat(collectedValue).isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java)
+        }
+
+    @Test
     fun `quickAffordance - bottom start affordance hidden while dozing`() =
         testScope.runTest {
             repository.setDozing(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 6afeddd..8bd8be5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.app.admin.DevicePolicyManager
 import android.content.Intent
 import android.os.UserHandle
 import androidx.test.filters.SmallTest
@@ -87,6 +88,7 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: KeyguardBottomAreaViewModel
 
@@ -140,6 +142,7 @@
                 bouncerRepository = FakeKeyguardBouncerRepository(),
             )
         whenever(userTracker.userHandle).thenReturn(mock())
+        whenever(userTracker.userId).thenReturn(10)
         whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
             .thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
         val testDispatcher = StandardTestDispatcher()
@@ -205,6 +208,8 @@
                         featureFlags = featureFlags,
                         repository = { quickAffordanceRepository },
                         launchAnimator = launchAnimator,
+                        devicePolicyManager = devicePolicyManager,
+                        backgroundDispatcher = testDispatcher,
                     ),
                 bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
                 burnInHelperWrapper = burnInHelperWrapper,
@@ -240,6 +245,39 @@
         }
 
     @Test
+    fun `startButton - hidden when device policy disables all keyguard features`() =
+        testScope.runTest {
+            whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
+            repository.setKeyguardShowing(true)
+            val latest by collectLastValue(underTest.startButton)
+
+            val testConfig =
+                TestConfig(
+                    isVisible = true,
+                    isClickable = true,
+                    isActivated = true,
+                    icon = mock(),
+                    canShowWhileLocked = false,
+                    intent = Intent("action"),
+                )
+            val configKey =
+                setUpQuickAffordanceModel(
+                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
+                    testConfig = testConfig,
+                )
+
+            assertQuickAffordanceViewModel(
+                viewModel = latest,
+                testConfig =
+                    TestConfig(
+                        isVisible = false,
+                    ),
+                configKey = configKey,
+            )
+        }
+
+    @Test
     fun `startButton - in preview mode - visible even when keyguard not showing`() =
         testScope.runTest {
             underTest.enablePreviewMode(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 6cf642c..09156d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -70,7 +70,7 @@
 
         whenever(brightnessSliderFactory.create(any(), any())).thenReturn(brightnessSlider)
         whenever(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
-        testableResources.addOverride(R.bool.config_use_split_notification_shade, false)
+        setShouldUseSplitShade(false)
         whenever(qsPanel.resources).thenReturn(testableResources.resources)
         whenever(qsPanel.getOrCreateTileLayout()).thenReturn(pagedTileLayout)
         whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false)
@@ -133,12 +133,31 @@
 
     @Test
     fun configurationChange_onlySplitShadeConfigChanges_tileAreRedistributed() {
-        testableResources.addOverride(R.bool.config_use_split_notification_shade, false)
+        setShouldUseSplitShade(false)
         controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
         verify(pagedTileLayout, never()).forceTilesRedistribution(any())
 
-        testableResources.addOverride(R.bool.config_use_split_notification_shade, true)
+        setShouldUseSplitShade(true)
         controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
         verify(pagedTileLayout).forceTilesRedistribution("Split shade state changed")
     }
+
+    @Test
+    fun configurationChange_onlySplitShadeConfigChanges_qsPanelCanBeCollapsed() {
+        setShouldUseSplitShade(false)
+        controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+        verify(qsPanel, never()).setCanCollapse(anyBoolean())
+
+        setShouldUseSplitShade(true)
+        controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+        verify(qsPanel).setCanCollapse(false)
+
+        setShouldUseSplitShade(false)
+        controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+        verify(qsPanel).setCanCollapse(true)
+    }
+
+    private fun setShouldUseSplitShade(shouldUse: Boolean) {
+        testableResources.addOverride(R.bool.config_use_split_notification_shade, shouldUse)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index d52b296..a8cfb25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -37,6 +37,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -196,6 +197,16 @@
         qsPanel.setSquishinessFraction(0.5f)
     }
 
+    @Test
+    fun testSplitShade_CollapseAccessibilityActionNotAnnounced() {
+        qsPanel.setCanCollapse(false)
+        val accessibilityInfo = mock(AccessibilityNodeInfo::class.java)
+        qsPanel.onInitializeAccessibilityNodeInfo(accessibilityInfo)
+
+        val actionCollapse = AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE
+        verify(accessibilityInfo, never()).addAction(actionCollapse)
+    }
+
     private infix fun View.isLeftOf(other: View): Boolean {
         val rect = Rect()
         getBoundsOnScreen(rect)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index 6175df9..e684007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -1,7 +1,6 @@
 package com.android.systemui.shade
 
 import android.animation.Animator
-import android.animation.ValueAnimator
 import android.app.StatusBarManager
 import android.content.Context
 import android.testing.AndroidTestingRunner
@@ -44,6 +43,7 @@
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.junit.MockitoJUnit
@@ -158,10 +158,20 @@
     fun updateListeners_registersWhenVisible() {
         makeShadeVisible()
         verify(qsCarrierGroupController).setListening(true)
+    }
+
+    @Test
+    fun statusIconsAddedWhenAttached() {
         verify(statusBarIconController).addIconGroup(any())
     }
 
     @Test
+    fun statusIconsRemovedWhenDettached() {
+        mLargeScreenShadeHeaderController.simulateViewDetached()
+        verify(statusBarIconController).removeIconGroup(any())
+    }
+
+    @Test
     fun shadeExpandedFraction_updatesAlpha() {
         makeShadeVisible()
         mLargeScreenShadeHeaderController.shadeExpandedFraction = 0.5f
@@ -169,16 +179,6 @@
     }
 
     @Test
-    fun alphaChangesUpdateVisibility() {
-        makeShadeVisible()
-        mLargeScreenShadeHeaderController.shadeExpandedFraction = 0f
-        assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
-
-        mLargeScreenShadeHeaderController.shadeExpandedFraction = 1f
-        assertThat(viewVisibility).isEqualTo(View.VISIBLE)
-    }
-
-    @Test
     fun singleCarrier_enablesCarrierIconsInStatusIcons() {
         whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
 
@@ -263,40 +263,32 @@
     }
 
     @Test
-    fun testShadeExpanded_true_alpha_zero_invisible() {
-        view.alpha = 0f
-        mLargeScreenShadeHeaderController.largeScreenActive = true
-        mLargeScreenShadeHeaderController.qsVisible = true
+    fun customizerAnimatorChangesViewVisibility() {
+        makeShadeVisible()
 
-        assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
-    }
-
-    @Test
-    fun animatorCallsUpdateVisibilityOnUpdate() {
         val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+        val duration = 1000L
         whenever(view.animate()).thenReturn(animator)
+        val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
 
-        mLargeScreenShadeHeaderController.startCustomizingAnimation(show = false, 0L)
-
-        val updateCaptor = argumentCaptor<ValueAnimator.AnimatorUpdateListener>()
-        verify(animator).setUpdateListener(capture(updateCaptor))
-
-        mLargeScreenShadeHeaderController.largeScreenActive = true
-        mLargeScreenShadeHeaderController.qsVisible = true
-
-        view.alpha = 1f
-        updateCaptor.value.onAnimationUpdate(mock())
-
-        assertThat(viewVisibility).isEqualTo(View.VISIBLE)
-
-        view.alpha = 0f
-        updateCaptor.value.onAnimationUpdate(mock())
-
+        mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, duration)
+        verify(animator).setListener(capture(listenerCaptor))
+        // Start and end the animation
+        listenerCaptor.value.onAnimationStart(mock())
+        listenerCaptor.value.onAnimationEnd(mock())
         assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
+
+        reset(animator)
+        mLargeScreenShadeHeaderController.startCustomizingAnimation(show = false, duration)
+        verify(animator).setListener(capture(listenerCaptor))
+        // Start and end the animation
+        listenerCaptor.value.onAnimationStart(mock())
+        listenerCaptor.value.onAnimationEnd(mock())
+        assertThat(viewVisibility).isEqualTo(View.VISIBLE)
     }
 
     @Test
-    fun animatorListenersClearedAtEnd() {
+    fun animatorListenerClearedAtEnd() {
         val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
         whenever(view.animate()).thenReturn(animator)
 
@@ -306,21 +298,6 @@
 
         listenerCaptor.value.onAnimationEnd(mock())
         verify(animator).setListener(null)
-        verify(animator).setUpdateListener(null)
-    }
-
-    @Test
-    fun animatorListenersClearedOnCancel() {
-        val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
-        whenever(view.animate()).thenReturn(animator)
-
-        mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L)
-        val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
-        verify(animator).setListener(capture(listenerCaptor))
-
-        listenerCaptor.value.onAnimationCancel(mock())
-        verify(animator).setListener(null)
-        verify(animator).setUpdateListener(null)
     }
 
     @Test
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 328b971..cde820a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -4577,6 +4577,17 @@
         return false;
     }
 
+    /**
+     * Called when always on magnification feature flag flips to check if the feature should be
+     * enabled for current user state.
+     */
+    public void updateAlwaysOnMagnification() {
+        synchronized (mLock) {
+            readAlwaysOnMagnificationLocked(getCurrentUserState());
+        }
+    }
+
+    @GuardedBy("mLock")
     boolean readAlwaysOnMagnificationLocked(AccessibilityUserState userState) {
         final boolean isSettingsAlwaysOnEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java b/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
index ed45e7b..16d2e6b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
@@ -16,10 +16,13 @@
 
 package com.android.server.accessibility.magnification;
 
+import android.annotation.NonNull;
 import android.provider.DeviceConfig;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.concurrent.Executor;
+
 /**
  * Encapsulates the feature flags for always on magnification. {@see DeviceConfig}
  *
@@ -50,4 +53,39 @@
                 Boolean.toString(isEnabled),
                 /* makeDefault= */ false);
     }
+
+    /**
+     * Adds a listener for when the feature flag changes.
+     *
+     * <p>{@see DeviceConfig#addOnPropertiesChangedListener(
+     * String, Executor, DeviceConfig.OnPropertiesChangedListener)}
+     */
+    @NonNull
+    public static DeviceConfig.OnPropertiesChangedListener addOnChangedListener(
+            @NonNull Executor executor, @NonNull Runnable listener) {
+        DeviceConfig.OnPropertiesChangedListener onChangedListener =
+                properties -> {
+                    if (properties.getKeyset().contains(
+                            FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION)) {
+                        listener.run();
+                    }
+                };
+        DeviceConfig.addOnPropertiesChangedListener(
+                NAMESPACE,
+                executor,
+                onChangedListener);
+
+        return onChangedListener;
+    }
+
+    /**
+     * Remove a listener for when the feature flag changes.
+     *
+     * <p>{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor,
+     * DeviceConfig.OnPropertiesChangedListener)}
+     */
+    public static void removeOnChangedListener(
+            @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) {
+        DeviceConfig.removeOnPropertiesChangedListener(onChangedListener);
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index a6e6bd7..4753a54 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -48,6 +48,7 @@
 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ConcurrentUtils;
 import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.wm.WindowManagerInternal;
@@ -149,6 +150,9 @@
                 .getAccessibilityController().setUiChangesForAccessibilityCallbacks(this);
         mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
                 FEATURE_WINDOW_MAGNIFICATION);
+
+        AlwaysOnMagnificationFeatureFlag.addOnChangedListener(
+                ConcurrentUtils.DIRECT_EXECUTOR, mAms::updateAlwaysOnMagnification);
     }
 
     @VisibleForTesting
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index e35e4b3..af6b24e 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1142,7 +1142,7 @@
             } else {
                 // For any widget other then the first one, we just send update intent
                 // as we normally would.
-                sendUpdateIntentLocked(provider, new int[]{appWidgetId});
+                sendUpdateIntentLocked(provider, new int[]{appWidgetId}, true);
             }
 
             // Schedule the future updates.
@@ -2379,7 +2379,7 @@
         if (!canSendCombinedBroadcast) {
             // If this function is called by mistake, send two separate broadcasts instead
             sendEnableIntentLocked(p);
-            sendUpdateIntentLocked(p, appWidgetIds);
+            sendUpdateIntentLocked(p, appWidgetIds, true);
             return;
         }
 
@@ -2399,12 +2399,12 @@
         sendBroadcastAsUser(intent, p.id.getProfile(), true);
     }
 
-    private void sendUpdateIntentLocked(Provider provider, int[] appWidgetIds) {
+    private void sendUpdateIntentLocked(Provider provider, int[] appWidgetIds,
+            boolean interactive) {
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
         intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
         intent.setComponent(provider.id.componentName);
-        // Periodic background widget update heartbeats are not an interactive use case
-        sendBroadcastAsUser(intent, provider.id.getProfile(), false);
+        sendBroadcastAsUser(intent, provider.id.getProfile(), interactive);
     }
 
     private void sendDeletedIntentLocked(Widget widget) {
@@ -3589,7 +3589,7 @@
                                 scheduleNotifyProviderChangedLocked(widget);
                             }
                             // Now that we've told the host, push out an update.
-                            sendUpdateIntentLocked(provider, appWidgetIds);
+                            sendUpdateIntentLocked(provider, appWidgetIds, false);
                         }
                     }
                     providersUpdated = true;
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 1c75380..e536076 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -159,6 +159,8 @@
     );
 
     public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] {
+            "android.hardware.audio.core.IModule/",
+            "android.hardware.audio.core.IConfig/",
             "android.hardware.biometrics.face.IFace/",
             "android.hardware.biometrics.fingerprint.IFingerprint/",
             "android.hardware.bluetooth.IBluetoothHci/",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ede4c5e..3a93cb3 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -7786,7 +7786,10 @@
                 r.mIsFgsDelegate,
                 r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mClientUid : INVALID_UID,
                 r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mDelegationService
-                        : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT);
+                        : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT,
+                0,
+                null,
+                null);
 
         int event = 0;
         if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 176690d..96d5f38 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18843,6 +18843,11 @@
         }
     }
 
+    @Override
+    public void waitForBroadcastBarrier() {
+        waitForBroadcastBarrier(/* printWriter= */ null, false);
+    }
+
     public void waitForBroadcastBarrier(@Nullable PrintWriter pw, boolean flushBroadcastLoopers) {
         enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()");
         if (flushBroadcastLoopers) {
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index fe5888d..53fcddf 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -225,7 +225,7 @@
     public int MAX_HISTORY_COMPLETE_SIZE = DEFAULT_MAX_HISTORY_COMPLETE_SIZE;
     private static final String KEY_MAX_HISTORY_COMPLETE_SIZE = "bcast_max_history_complete_size";
     private static final int DEFAULT_MAX_HISTORY_COMPLETE_SIZE =
-            ActivityManager.isLowRamDeviceStatic() ? 10 : 50;
+            ActivityManager.isLowRamDeviceStatic() ? 64 : 256;
 
     /**
      * For {@link BroadcastQueueModernImpl}: Maximum number of summarized
@@ -234,16 +234,7 @@
     public int MAX_HISTORY_SUMMARY_SIZE = DEFAULT_MAX_HISTORY_SUMMARY_SIZE;
     private static final String KEY_MAX_HISTORY_SUMMARY_SIZE = "bcast_max_history_summary_size";
     private static final int DEFAULT_MAX_HISTORY_SUMMARY_SIZE =
-            ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
-
-    /**
-     * For {@link BroadcastQueueModernImpl}: Maximum number of broadcast receivers to process in a
-     * single synchronized block.  Up to this many messages may be dispatched in a single binder
-     * call.  Set this to 1 (or zero) for pre-batch behavior.
-     */
-    public int MAX_BROADCAST_BATCH_SIZE = DEFAULT_MAX_BROADCAST_BATCH_SIZE;
-    private static final String KEY_MAX_BROADCAST_BATCH_SIZE = "bcast_max_batch_size";
-    private static final int DEFAULT_MAX_BROADCAST_BATCH_SIZE = 1;
+            ActivityManager.isLowRamDeviceStatic() ? 256 : 1024;
 
     // Settings override tracking for this instance
     private String mSettingsKey;
@@ -382,8 +373,6 @@
                     DEFAULT_MAX_HISTORY_COMPLETE_SIZE);
             MAX_HISTORY_SUMMARY_SIZE = getDeviceConfigInt(KEY_MAX_HISTORY_SUMMARY_SIZE,
                     DEFAULT_MAX_HISTORY_SUMMARY_SIZE);
-            MAX_BROADCAST_BATCH_SIZE = getDeviceConfigInt(KEY_MAX_BROADCAST_BATCH_SIZE,
-                    DEFAULT_MAX_BROADCAST_BATCH_SIZE);
         }
     }
 
@@ -429,7 +418,6 @@
                     MAX_CONSECUTIVE_URGENT_DISPATCHES).println();
             pw.print(KEY_MAX_CONSECUTIVE_NORMAL_DISPATCHES,
                     MAX_CONSECUTIVE_NORMAL_DISPATCHES).println();
-            pw.print(KEY_MAX_BROADCAST_BATCH_SIZE, MAX_BROADCAST_BATCH_SIZE).println();
             pw.decreaseIndent();
             pw.println();
         }
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 85d535b..c085706 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -76,7 +76,6 @@
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.TimeoutRecord;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
@@ -190,14 +189,6 @@
         }
     }
 
-    /**
-     * This single object allows the queue to dispatch receivers using scheduleReceiverList
-     * without constantly allocating new ReceiverInfo objects or ArrayLists.  This queue
-     * implementation is known to have a maximum size of one entry.
-     */
-    @VisibleForTesting
-    final BroadcastReceiverBatch mReceiverBatch = new BroadcastReceiverBatch(1);
-
     BroadcastQueueImpl(ActivityManagerService service, Handler handler,
             String name, BroadcastConstants constants, boolean allowDelayBehindServices,
             int schedGroup) {
@@ -402,13 +393,13 @@
             mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
                                       PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
             final boolean assumeDelivered = false;
-            thread.scheduleReceiverList(mReceiverBatch.manifestReceiver(
+            thread.scheduleReceiver(
                     prepareReceiverIntent(r.intent, r.curFilteredExtras),
                     r.curReceiver, null /* compatInfo (unused but need to keep method signature) */,
                     r.resultCode, r.resultData, r.resultExtras, r.ordered, assumeDelivered,
                     r.userId, r.shareIdentity ? r.callingUid : Process.INVALID_UID,
-                    r.shareIdentity ? r.callerPackage : null,
-                    app.mState.getReportedProcState()));
+                    app.mState.getReportedProcState(),
+                    r.shareIdentity ? r.callerPackage : null);
             if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                     "Process cur broadcast " + r + " DELIVERED for app " + app);
             started = true;
@@ -756,12 +747,12 @@
                 // correctly ordered with other one-way calls.
                 try {
                     final boolean assumeDelivered = !ordered;
-                    thread.scheduleReceiverList(mReceiverBatch.registeredReceiver(
+                    thread.scheduleRegisteredReceiver(
                             receiver, intent, resultCode,
                             data, extras, ordered, sticky, assumeDelivered, sendingUser,
+                            app.mState.getReportedProcState(),
                             shareIdentity ? callingUid : Process.INVALID_UID,
-                            shareIdentity ? callingPackage : null,
-                            app.mState.getReportedProcState()));
+                            shareIdentity ? callingPackage : null);
                 } catch (RemoteException ex) {
                     // Failed to call into the process. It's either dying or wedged. Kill it gently.
                     synchronized (mService) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index b016890..5c7fde7 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -145,11 +145,6 @@
         // We configure runnable size only once at boot; it'd be too complex to
         // try resizing dynamically at runtime
         mRunning = new BroadcastProcessQueue[mConstants.getMaxRunningQueues()];
-
-        // Set up the statistics for batched broadcasts.
-        final int batchSize = mConstants.MAX_BROADCAST_BATCH_SIZE;
-        mReceiverBatch = new BroadcastReceiverBatch(batchSize);
-        Slog.i(TAG, "maximum broadcast batch size " + batchSize);
     }
 
     /**
@@ -220,15 +215,6 @@
     private final BroadcastConstants mBgConstants;
 
     /**
-     * The sole instance of BroadcastReceiverBatch that is used by scheduleReceiverWarmLocked().
-     * The class is not a true singleton but only one instance is needed for the broadcast queue.
-     * Although this is guarded by mService, it should never be accessed by any other function.
-     */
-    @VisibleForTesting
-    @GuardedBy("mService")
-    final BroadcastReceiverBatch mReceiverBatch;
-
-    /**
      * Timestamp when last {@link #testAllProcessQueues} failure was observed;
      * used for throttling log messages.
      */
@@ -248,19 +234,10 @@
 
     private void enqueueFinishReceiver(@NonNull BroadcastProcessQueue queue,
             @DeliveryState int deliveryState, @NonNull String reason) {
-        enqueueFinishReceiver(queue, queue.getActive(), queue.getActiveIndex(),
-                deliveryState, reason);
-    }
-
-    private void enqueueFinishReceiver(@NonNull BroadcastProcessQueue queue,
-            @NonNull BroadcastRecord r, int index,
-            @DeliveryState int deliveryState, @NonNull String reason) {
         final SomeArgs args = SomeArgs.obtain();
         args.arg1 = queue;
         args.argi1 = deliveryState;
         args.arg2 = reason;
-        args.arg3 = r;
-        args.argi2 = index;
         mLocalHandler.sendMessage(Message.obtain(mLocalHandler, MSG_FINISH_RECEIVER, args));
     }
 
@@ -308,10 +285,8 @@
                     final BroadcastProcessQueue queue = (BroadcastProcessQueue) args.arg1;
                     final int deliveryState = args.argi1;
                     final String reason = (String) args.arg2;
-                    final BroadcastRecord r = (BroadcastRecord) args.arg3;
-                    final int index = args.argi2;
                     args.recycle();
-                    finishReceiverLocked(queue, deliveryState, reason, r, index);
+                    finishReceiverActiveLocked(queue, deliveryState, reason);
                 }
                 return true;
             }
@@ -767,7 +742,7 @@
             return;
         }
 
-        if (maybeSkipReceiver(queue, null, r, index)) {
+        if (maybeSkipReceiver(queue, r, index)) {
             mRunningColdStart = null;
             return;
         }
@@ -811,22 +786,20 @@
     @GuardedBy("mService")
     private void scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue) {
         checkState(queue.isActive(), "isActive");
-        BroadcastReceiverBatch batch = mReceiverBatch;
-        batch.reset();
 
-        while (collectReceiverList(queue, batch)) {
-            if (batch.isFull()) {
-                break;
-            }
-            if (!shouldContinueScheduling(queue)) {
-                break;
-             }
-            if (queue.isEmpty()) {
-                break;
-            }
-            queue.makeActiveNextPending();
+        final BroadcastRecord r = queue.getActive();
+        final int index = queue.getActiveIndex();
+
+        if (r.terminalCount == 0) {
+            r.dispatchTime = SystemClock.uptimeMillis();
+            r.dispatchRealTime = SystemClock.elapsedRealtime();
+            r.dispatchClockTime = System.currentTimeMillis();
         }
-        processReceiverList(queue, batch);
+
+        if (maybeSkipReceiver(queue, r, index)) {
+            return;
+        }
+        dispatchReceivers(queue, r, index);
     }
 
     /**
@@ -834,14 +807,10 @@
      * skipped (and therefore no more work is required).
      */
     private boolean maybeSkipReceiver(@NonNull BroadcastProcessQueue queue,
-            @Nullable BroadcastReceiverBatch batch, @NonNull BroadcastRecord r, int index) {
+            @NonNull BroadcastRecord r, int index) {
         final String reason = shouldSkipReceiver(queue, r, index);
         if (reason != null) {
-            if (batch == null) {
-                enqueueFinishReceiver(queue, r, index, BroadcastRecord.DELIVERY_SKIPPED, reason);
-            } else {
-                batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED, reason);
-            }
+            enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_SKIPPED, reason);
             return true;
         }
         return false;
@@ -884,147 +853,6 @@
     }
 
     /**
-     * Collect receivers into a list, to be dispatched in a single receiver list call.  Return
-     * true if remaining receivers in the queue should be examined, and false if the current list
-     * is complete.
-     */
-    private boolean collectReceiverList(@NonNull BroadcastProcessQueue queue,
-            @NonNull BroadcastReceiverBatch batch) {
-        final ProcessRecord app = queue.app;
-        final BroadcastRecord r = queue.getActive();
-        final int index = queue.getActiveIndex();
-        final Object receiver = r.receivers.get(index);
-        final Intent receiverIntent = r.getReceiverIntent(receiver);
-
-        if (r.terminalCount == 0) {
-            r.dispatchTime = SystemClock.uptimeMillis();
-            r.dispatchRealTime = SystemClock.elapsedRealtime();
-            r.dispatchClockTime = System.currentTimeMillis();
-        }
-        if (maybeSkipReceiver(queue, batch, r, index)) {
-            return true;
-        }
-
-        final IApplicationThread thread = app.getOnewayThread();
-        if (thread == null) {
-            batch.finish(r, index, BroadcastRecord.DELIVERY_FAILURE, "missing IApplicationThread");
-            return true;
-        }
-
-        final boolean assumeDelivered = isAssumedDelivered(r, index);
-        if (receiver instanceof BroadcastFilter) {
-            batch.schedule(((BroadcastFilter) receiver).receiverList.receiver,
-                    receiverIntent, r.resultCode, r.resultData, r.resultExtras,
-                    r.ordered, r.initialSticky, assumeDelivered, r.userId,
-                    r.shareIdentity ? r.callingUid : Process.INVALID_UID,
-                    r.shareIdentity ? r.callerPackage : null,
-                    app.mState.getReportedProcState(), r, index);
-            // TODO: consider making registered receivers of unordered
-            // broadcasts report results to detect ANRs
-            if (assumeDelivered) {
-                batch.success(r, index, BroadcastRecord.DELIVERY_DELIVERED, "assuming delivered");
-                return true;
-            }
-        } else {
-            batch.schedule(receiverIntent, ((ResolveInfo) receiver).activityInfo,
-                    null, r.resultCode, r.resultData, r.resultExtras, r.ordered, assumeDelivered,
-                    r.userId, r.shareIdentity ? r.callingUid : Process.INVALID_UID,
-                    r.shareIdentity ? r.callerPackage : null,
-                    app.mState.getReportedProcState(), r, index);
-            if (assumeDelivered) {
-                batch.success(r, index, BroadcastRecord.DELIVERY_DELIVERED, "assuming delivered");
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Process the information in a BroadcastReceiverBatch.  Elements in the finish and success
-     * lists are sent to enqueueFinishReceiver().  Elements in the receivers list are transmitted
-     * to the target in a single binder call.
-     */
-    private void processReceiverList(@NonNull BroadcastProcessQueue queue,
-            @NonNull BroadcastReceiverBatch batch) {
-        // Transmit the receiver list.
-        final ProcessRecord app = queue.app;
-        final IApplicationThread thread = app.getOnewayThread();
-
-        batch.recordBatch(thread instanceof SameProcessApplicationThread);
-
-        // Mark all the receivers that were discarded.  None of these have actually been scheduled.
-        for (int i = 0; i < batch.finished().size(); i++) {
-            final var finish = batch.finished().get(i);
-            enqueueFinishReceiver(queue, finish.r, finish.index, finish.deliveryState,
-                    finish.reason);
-        }
-        // Prepare for delivery of all receivers that are about to be scheduled.
-        for (int i = 0; i < batch.cookies().size(); i++) {
-            final var cookie = batch.cookies().get(i);
-            prepareToDispatch(queue, cookie.r, cookie.index);
-        }
-
-        // Notify on dispatch.  Note that receiver/cookies are recorded only if the thread is
-        // non-null and the list will therefore be sent.
-        for (int i = 0; i < batch.cookies().size(); i++) {
-            // Cookies and receivers are 1:1
-            final var cookie = batch.cookies().get(i);
-            final BroadcastRecord r = cookie.r;
-            final int index = cookie.index;
-            final Object receiver = r.receivers.get(index);
-
-            if (r.shareIdentity) {
-                mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent,
-                        UserHandle.getAppId(app.uid), r.callingUid, true);
-            }
-            if (receiver instanceof BroadcastFilter) {
-                notifyScheduleRegisteredReceiver(queue.app, r, (BroadcastFilter) receiver);
-            } else {
-                notifyScheduleReceiver(queue.app, r, (ResolveInfo) receiver);
-            }
-        }
-
-        // Transmit the enqueued receivers.  The thread cannot be null because the lock has been
-        // held since collectReceiverList(), which will not add any receivers if the thread is null.
-        boolean remoteFailed = false;
-        if (batch.receivers().size()  > 0) {
-            try {
-                thread.scheduleReceiverList(batch.receivers());
-            } catch (RemoteException e) {
-                // Log the failure of the first receiver in the list.  Note that there must be at
-                // least one receiver/cookie to reach this point in the code, which means
-                // cookie[0] is a valid element.
-                final var info = batch.cookies().get(0);
-                final BroadcastRecord r = info.r;
-                final int index = info.index;
-                final Object receiver = r.receivers.get(index);
-                final String msg = "Failed to schedule " + r + " to " + receiver
-                                   + " via " + app + ": " + e;
-                logw(msg);
-                app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER, true);
-                remoteFailed = true;
-            }
-        }
-
-        if (!remoteFailed) {
-            // If transmission succeed, report all receivers that are assumed to be delivered.
-            for (int i = 0; i < batch.success().size(); i++) {
-                final var finish = batch.success().get(i);
-                enqueueFinishReceiver(queue, finish.r, finish.index, finish.deliveryState,
-                        finish.reason);
-            }
-        } else {
-            // If transmission failed, fail all receivers in the list.
-            for (int i = 0; i < batch.cookies().size(); i++) {
-                final var cookie = batch.cookies().get(i);
-                enqueueFinishReceiver(queue, cookie.r, cookie.index,
-                        BroadcastRecord.DELIVERY_FAILURE, "remote app");
-            }
-        }
-    }
-
-    /**
      * Return true if this receiver should be assumed to have been delivered.
      */
     private boolean isAssumedDelivered(BroadcastRecord r, int index) {
@@ -1035,7 +863,7 @@
     /**
      * A receiver is about to be dispatched.  Start ANR timers, if necessary.
      */
-    private void prepareToDispatch(@NonNull BroadcastProcessQueue queue,
+    private void dispatchReceivers(@NonNull BroadcastProcessQueue queue,
             @NonNull BroadcastRecord r, int index) {
         final ProcessRecord app = queue.app;
         final Object receiver = r.receivers.get(index);
@@ -1074,6 +902,47 @@
         if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app);
         setDeliveryState(queue, app, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED,
                 "scheduleReceiverWarmLocked");
+
+        final Intent receiverIntent = r.getReceiverIntent(receiver);
+        final IApplicationThread thread = app.getOnewayThread();
+        if (thread != null) {
+            try {
+                if (receiver instanceof BroadcastFilter) {
+                    notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver);
+                    thread.scheduleRegisteredReceiver(
+                        ((BroadcastFilter) receiver).receiverList.receiver,
+                        receiverIntent, r.resultCode, r.resultData, r.resultExtras,
+                        r.ordered, r.initialSticky, assumeDelivered, r.userId,
+                        app.mState.getReportedProcState(),
+                        r.shareIdentity ? r.callingUid : Process.INVALID_UID,
+                        r.shareIdentity ? r.callerPackage : null);
+                    // TODO: consider making registered receivers of unordered
+                    // broadcasts report results to detect ANRs
+                    if (assumeDelivered) {
+                        enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_DELIVERED,
+                                "assuming delivered");
+                    }
+                } else {
+                    notifyScheduleReceiver(app, r, (ResolveInfo) receiver);
+                    thread.scheduleReceiver(receiverIntent, ((ResolveInfo) receiver).activityInfo,
+                            null, r.resultCode, r.resultData, r.resultExtras, r.ordered,
+                            assumeDelivered, r.userId,
+                            app.mState.getReportedProcState(),
+                            r.shareIdentity ? r.callingUid : Process.INVALID_UID,
+                            r.shareIdentity ? r.callerPackage : null);
+                }
+            } catch (RemoteException e) {
+                final String msg = "Failed to schedule " + r + " to " + receiver
+                        + " via " + app + ": " + e;
+                logw(msg);
+                app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
+                        ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
+                enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE, "remote app");
+            }
+        } else {
+            enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE,
+                    "missing IApplicationThread");
+        }
     }
 
     /**
@@ -1093,13 +962,13 @@
             }
             try {
                 final boolean assumeDelivered = true;
-                thread.scheduleReceiverList(mReceiverBatch.registeredReceiver(
+                thread.scheduleRegisteredReceiver(
                         r.resultTo, r.intent,
                         r.resultCode, r.resultData, r.resultExtras, false, r.initialSticky,
                         assumeDelivered, r.userId,
+                        app.mState.getReportedProcState(),
                         r.shareIdentity ? r.callingUid : Process.INVALID_UID,
-                        r.shareIdentity ? r.callerPackage : null,
-                        app.mState.getReportedProcState()));
+                        r.shareIdentity ? r.callerPackage : null);
             } catch (RemoteException e) {
                 final String msg = "Failed to schedule result of " + r + " via " + app + ": " + e;
                 logw(msg);
@@ -1187,33 +1056,20 @@
             return false;
         }
 
-        final BroadcastRecord r = queue.getActive();
-        final int index = queue.getActiveIndex();
-        return finishReceiverLocked(queue, deliveryState, reason, r, index);
-    }
-
-    private boolean finishReceiverLocked(@NonNull BroadcastProcessQueue queue,
-            @DeliveryState int deliveryState, @NonNull String reason,
-            BroadcastRecord r, int index) {
-        if (!queue.isActive()) {
-            logw("Ignoring finish; no active broadcast for " + queue);
-            return false;
-        }
-
         final int cookie = traceBegin("finishReceiver");
         final ProcessRecord app = queue.app;
+        final BroadcastRecord r = queue.getActive();
+        final int index = queue.getActiveIndex();
         final Object receiver = r.receivers.get(index);
 
         setDeliveryState(queue, app, r, index, receiver, deliveryState, reason);
 
-        final boolean early = r != queue.getActive() || index != queue.getActiveIndex();
-
         if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
             r.anrCount++;
             if (app != null && !app.isDebugging()) {
                 mService.appNotResponding(queue.app, TimeoutRecord.forBroadcastReceiver(r.intent));
             }
-        } else if (!early) {
+        } else {
             mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);
             mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_HARD, queue);
         }
@@ -1221,13 +1077,6 @@
         // Given that a receiver just finished, check if the "waitingFor" conditions are met.
         checkAndRemoveWaitingFor();
 
-        if (early) {
-            // This is an early receiver that was transmitted as part of a group.  The delivery
-            // state has been updated but don't make any further decisions.
-            traceEnd(cookie);
-            return false;
-        }
-
         final boolean res = shouldContinueScheduling(queue);
         if (res) {
             // We're on a roll; move onto the next broadcast for this process
@@ -1980,17 +1829,6 @@
         ipw.decreaseIndent();
         ipw.println();
 
-        ipw.println("Batch statistics:");
-        ipw.increaseIndent();
-        {
-            final var stats = mReceiverBatch.getStatistics();
-            ipw.println("Finished         " + Arrays.toString(stats.finish));
-            ipw.println("DispatchedLocal  " + Arrays.toString(stats.local));
-            ipw.println("DispatchedRemote " + Arrays.toString(stats.remote));
-        }
-        ipw.decreaseIndent();
-        ipw.println();
-
         if (dumpConstants) {
             mConstants.dump(ipw);
         }
diff --git a/services/core/java/com/android/server/am/BroadcastReceiverBatch.java b/services/core/java/com/android/server/am/BroadcastReceiverBatch.java
deleted file mode 100644
index 63575ba..0000000
--- a/services/core/java/com/android/server/am/BroadcastReceiverBatch.java
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ReceiverInfo;
-import android.content.IIntentReceiver;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.res.CompatibilityInfo;
-import android.os.Bundle;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * A batch of receiver instructions. This includes a list of finish requests and a list of
- * receivers.  The instructions are for a single queue.  It is constructed and consumed in a single
- * call to {@link BroadcastQueueModernImpl#scheduleReceiverWarmLocked}.  The list size is bounded by
- * {@link BroadcastConstants#MAX_BROADCAST_BATCH_SIZE}.  Because this class is ephemeral and its use
- * is bounded, it is pre-allocated to avoid allocating new objects every time it is used.
- *
- * The {@link #single} methods support the use of this class in {@link BroadcastQueueImpl}.  These
- * methods simplify the use of {@link IApplicationThread#scheduleReceiverList} as a replacement
- * for scheduleReceiver and scheduleRegisteredReceiver.
- *
- * This class is designed to be allocated once and constantly reused (call {@link #reset} between
- * uses).  Objects needed by the instance are kept in a pool - all object allocation occurs when
- * the instance is created, and no allocation occurs thereafter.  However, if the variable
- * mDeepReceiverCopy is set true (it is false by default) then the method {@link #receivers} always
- * returns newly allocated objects.  This is required for mock testing.
- *
- * This class is not thread-safe.  Instances must be protected by the caller.
- * @hide
- */
-final class BroadcastReceiverBatch {
-
-    /**
-     * If this is true then receivers() returns a deep copy of the ReceiveInfo array.  If this is
-     * false, receivers() returns a reference to the array.  A deep copy is needed only for the
-     * broadcast queue mocking tests.
-     */
-    @VisibleForTesting
-    boolean mDeepReceiverCopy = false;
-
-    /**
-     * A private pool implementation this class.
-     */
-    private static class Pool<T> {
-        final int size;
-        final ArrayList<T> pool;
-        int next;
-        Pool(int n, @NonNull Class<T> c) {
-            size = n;
-            pool = new ArrayList<>(size);
-            try {
-                for (int i = 0; i < size; i++) {
-                    pool.add(c.getDeclaredConstructor().newInstance());
-                }
-            } catch (Exception e) {
-                // This class is only used locally.  Turn any exceptions into something fatal.
-                throw new RuntimeException(e);
-            }
-        }
-        T next() {
-            return pool.get(next++);
-        }
-        void reset() {
-            next = 0;
-        }
-    }
-
-    /**
-     * The information needed to finish off a receiver.  This is valid only in the context of
-     * a queue.
-     */
-    static class FinishInfo {
-        BroadcastRecord r = null;
-        int index = 0;
-        int deliveryState = 0;
-        String reason = null;
-        FinishInfo set(@Nullable BroadcastRecord r, int index,
-                int deliveryState, @Nullable String reason) {
-            this.r = r;
-            this.index = index;
-            this.deliveryState = deliveryState;
-            this.reason = reason;
-            return this;
-        }
-    }
-
-    /**
-     * The information needed to recreate a receiver info.  The broadcast record can be null if the
-     * caller does not expect to need it later.
-     */
-    static class ReceiverCookie {
-        BroadcastRecord r = null;
-        int index = 0;
-        ReceiverCookie set(@Nullable BroadcastRecord r, int index) {
-            this.r = r;
-            this.index = index;
-            return this;
-        }
-    }
-
-    // The object pools.
-    final int mSize;
-    private final Pool<ReceiverInfo> receiverPool;
-    private final Pool<FinishInfo> finishPool;
-    private final Pool<ReceiverCookie> cookiePool;
-
-    // The accumulated data.  The receivers should be an ArrayList to be directly compatible
-    // with scheduleReceiverList().  The receivers array is not final because a new array must
-    // be created for every new call to scheduleReceiverList().
-    private final ArrayList<ReceiverInfo> mReceivers;
-    private final ArrayList<ReceiverCookie> mCookies;
-    private final ArrayList<FinishInfo> mFinished;
-    // The list of finish records to complete if the binder succeeds or fails.
-    private final ArrayList<FinishInfo> mSuccess;
-
-    BroadcastReceiverBatch(int size) {
-        mSize = size;
-        mReceivers = new ArrayList<>(mSize);
-        mCookies = new ArrayList<>(mSize);
-        mFinished = new ArrayList<>(mSize);
-        mSuccess = new ArrayList<>(mSize);
-
-        receiverPool = new Pool<>(mSize, ReceiverInfo.class);
-        finishPool = new Pool<>(mSize, FinishInfo.class);
-        cookiePool = new Pool<>(mSize, ReceiverCookie.class);
-        mStats = new Statistics(mSize);
-        reset();
-    }
-
-    void reset() {
-        mReceivers.clear();
-        mCookies.clear();
-        mFinished.clear();
-        mSuccess.clear();
-
-        receiverPool.reset();
-        finishPool.reset();
-        cookiePool.reset();
-    }
-
-    void finish(@Nullable BroadcastRecord r, int index,
-            int deliveryState, @Nullable String reason) {
-        mFinished.add(finishPool.next().set(r, index, deliveryState, reason));
-    }
-    void success(@Nullable BroadcastRecord r, int index,
-            int deliveryState, @Nullable String reason) {
-        mSuccess.add(finishPool.next().set(r, index, deliveryState, reason));
-    }
-    // Add a ReceiverInfo for a registered receiver.
-    void schedule(@Nullable IIntentReceiver receiver, Intent intent,
-            int resultCode, @Nullable String data, @Nullable Bundle extras, boolean ordered,
-            boolean sticky, boolean assumeDelivered, int sendingUser, int sendingUid,
-            @Nullable String sendingPackage, int processState, @Nullable BroadcastRecord r,
-            int index) {
-        ReceiverInfo ri = new ReceiverInfo();
-        ri.intent = intent;
-        ri.data = data;
-        ri.extras = extras;
-        ri.assumeDelivered = assumeDelivered;
-        ri.sendingUser = sendingUser;
-        ri.processState = processState;
-        ri.resultCode = resultCode;
-        ri.registered = true;
-        ri.receiver = receiver;
-        ri.ordered = ordered;
-        ri.sticky = sticky;
-        ri.sendingUid = sendingUid;
-        ri.sendingPackage = sendingPackage;
-
-        mReceivers.add(ri);
-        mCookies.add(cookiePool.next().set(r, index));
-    }
-    // Add a ReceiverInfo for a manifest receiver.
-    void schedule(@Nullable Intent intent, @Nullable ActivityInfo activityInfo,
-            @Nullable CompatibilityInfo compatInfo, int resultCode, @Nullable String data,
-            @Nullable Bundle extras, boolean sync, boolean assumeDelivered, int sendingUser,
-            int sendingUid, @Nullable String sendingPackage, int processState,
-            @Nullable BroadcastRecord r, int index) {
-        ReceiverInfo ri = new ReceiverInfo();
-        ri.intent = intent;
-        ri.data = data;
-        ri.extras = extras;
-        ri.assumeDelivered = assumeDelivered;
-        ri.sendingUser = sendingUser;
-        ri.processState = processState;
-        ri.resultCode = resultCode;
-        ri.registered = false;
-        ri.activityInfo = activityInfo;
-        ri.compatInfo = compatInfo;
-        ri.sync = sync;
-        ri.sendingUid = sendingUid;
-        ri.sendingPackage = sendingPackage;
-        mReceivers.add(ri);
-        mCookies.add(cookiePool.next().set(r, index));
-    }
-
-    /**
-     * Two convenience functions for dispatching a single receiver.  The functions start with a
-     * reset.  Then they create the ReceiverInfo array and return it.  Statistics are not
-     * collected.
-     */
-    ArrayList<ReceiverInfo> registeredReceiver(@Nullable IIntentReceiver receiver,
-            @Nullable Intent intent, int resultCode, @Nullable String data,
-            @Nullable Bundle extras, boolean ordered, boolean sticky, boolean assumeDelivered,
-            int sendingUser, int sendingUid, @Nullable String sendingPackage, int processState) {
-        reset();
-        schedule(receiver, intent, resultCode, data, extras, ordered, sticky, assumeDelivered,
-                sendingUser, sendingUid, sendingPackage, processState, null, 0);
-        return receivers();
-    }
-
-    ArrayList<ReceiverInfo> manifestReceiver(@Nullable Intent intent,
-            @Nullable ActivityInfo activityInfo, @Nullable CompatibilityInfo compatInfo,
-            int resultCode, @Nullable String data, @Nullable Bundle extras, boolean sync,
-            boolean assumeDelivered, int sendingUser, int sendingUid,
-            @Nullable String sendingPackage, int processState) {
-        reset();
-        schedule(intent, activityInfo, compatInfo, resultCode, data, extras, sync, assumeDelivered,
-                sendingUser, sendingUid, sendingPackage, processState, null, 0);
-        return receivers();
-    }
-
-    // Return true if the batch is full.  Adding any more entries will throw an exception.
-    boolean isFull() {
-        return (mFinished.size() + mReceivers.size()) >= mSize;
-    }
-    int finishCount() {
-        return mFinished.size() + mSuccess.size();
-    }
-    int receiverCount() {
-        return mReceivers.size();
-    }
-
-    /**
-     * Create a deep copy of the receiver list.  This is only for testing which is confused when
-     * objects are reused.
-     */
-    private ArrayList<ReceiverInfo> copyReceiverInfo() {
-        ArrayList<ReceiverInfo> copy = new ArrayList<>();
-        for (int i = 0; i < mReceivers.size(); i++) {
-            final ReceiverInfo r = mReceivers.get(i);
-            final ReceiverInfo n = new ReceiverInfo();
-            n.intent = r.intent;
-            n.data = r.data;
-            n.extras = r.extras;
-            n.assumeDelivered = r.assumeDelivered;
-            n.sendingUser = r.sendingUser;
-            n.processState = r.processState;
-            n.resultCode = r.resultCode;
-            n.registered = r.registered;
-            n.receiver = r.receiver;
-            n.ordered = r.ordered;
-            n.sticky = r.sticky;
-            n.activityInfo = r.activityInfo;
-            n.compatInfo = r.compatInfo;
-            n.sync = r.sync;
-            copy.add(n);
-        }
-        return copy;
-    }
-
-    /**
-     * Accessors for the accumulated instructions.  The important accessor is receivers(), since
-     * it can be modified to return a deep copy of the mReceivers array.
-     */
-    @NonNull
-    ArrayList<ReceiverInfo> receivers() {
-        if (!mDeepReceiverCopy) {
-            return mReceivers;
-        } else {
-            return copyReceiverInfo();
-        }
-    }
-    @NonNull
-    ArrayList<ReceiverCookie> cookies() {
-        return mCookies;
-    }
-    @NonNull
-    ArrayList<FinishInfo> finished() {
-        return mFinished;
-    }
-    @NonNull
-    ArrayList<FinishInfo> success() {
-        return mSuccess;
-    }
-
-    /**
-     * A simple POD for statistics.  The parameter is the size of the BroadcastReceiverBatch.
-     */
-    static class Statistics {
-        final int[] finish;
-        final int[] local;
-        final int[] remote;
-        Statistics(int size) {
-            finish = new int[size+1];
-            local = new int[size+1];
-            remote = new int[size+1];
-        }
-    }
-
-    private final Statistics mStats;
-
-    /**
-     * A unique counter that identifies individual transmission groups.  This is only used for
-     * debugging.  It is used to determine which receivers were sent in the same batch, and in
-     * which order.  This is static to distinguish between batches across all queues in the
-     * system.
-     */
-    private static final AtomicInteger sTransmitGroup = new AtomicInteger(0);
-
-    /**
-     * Record statistics for this batch of instructions.  This updates the local statistics and it
-     * updates the transmitGroup and transmitOrder fields of the BroadcastRecords being
-     * dispatched.
-     */
-    void recordBatch(boolean local) {
-        final int group = sTransmitGroup.addAndGet(1);
-        for (int i = 0; i < cookies().size(); i++) {
-            final var cookie = cookies().get(i);
-            cookie.r.transmitGroup[cookie.index] = group;
-            cookie.r.transmitOrder[cookie.index] = i;
-        }
-        mStats.finish[finishCount()]++;
-        if (local) {
-            mStats.local[receiverCount()]++;
-        } else {
-            mStats.remote[receiverCount()]++;
-        }
-    }
-
-    @NonNull
-    Statistics getStatistics() {
-        return mStats;
-    }
-}
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index bc2077a..67783be 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -120,8 +120,6 @@
     @UptimeMillisLong       long finishTime;         // when broadcast finished
     final @UptimeMillisLong long[] scheduledTime;    // when each receiver was scheduled
     final @UptimeMillisLong long[] terminalTime;     // when each receiver was terminal
-    final                   int[] transmitGroup;     // the batch group for each receiver
-    final                   int[] transmitOrder;     // the position of the receiver in the group
     final boolean timeoutExempt;  // true if this broadcast is not subject to receiver timeouts
     int resultCode;         // current result code value.
     @Nullable String resultData;      // current result data value.
@@ -400,8 +398,6 @@
         blockedUntilTerminalCount = calculateBlockedUntilTerminalCount(receivers, _serialized);
         scheduledTime = new long[delivery.length];
         terminalTime = new long[delivery.length];
-        transmitGroup = new int[delivery.length];
-        transmitOrder = new int[delivery.length];
         resultToApp = _resultToApp;
         resultTo = _resultTo;
         resultCode = _resultCode;
@@ -457,8 +453,6 @@
         blockedUntilTerminalCount = from.blockedUntilTerminalCount;
         scheduledTime = from.scheduledTime;
         terminalTime = from.terminalTime;
-        transmitGroup = from.transmitGroup;
-        transmitOrder = from.transmitOrder;
         resultToApp = from.resultToApp;
         resultTo = from.resultTo;
         enqueueTime = from.enqueueTime;
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index bd1cbee..c0cb7d9 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -25,9 +25,11 @@
 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_MICROPHONE;
 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_PHONE_CALL;
 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_USB;
+import static android.os.Process.INVALID_UID;
 
 import android.annotation.IntDef;
 import android.app.ActivityManager.ForegroundServiceApiType;
+import android.app.ForegroundServiceDelegationOptions;
 import android.content.ComponentName;
 import android.content.pm.ServiceInfo;
 import android.util.ArrayMap;
@@ -35,6 +37,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -430,33 +433,32 @@
     public void logFgsApiEvent(ServiceRecord r, int fgsState,
             @FgsApiState int apiState,
             @ForegroundServiceApiType int[] apiType, long[] timestamp) {
-        // TODO: Uncomment when atom changes are in
-//        FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
-//                r.appInfo.uid,
-//                r.shortInstanceName,
-//                fgsState, // FGS State
-//                r.mAllowWhileInUsePermissionInFgs, // allowWhileInUsePermissionInFgs
-//                r.mAllowStartForeground, // fgsStartReasonCode
-//                r.appInfo.targetSdkVersion,
-//                r.mRecentCallingUid,
-//                0, // callerTargetSdkVersion
-//                r.mInfoTempFgsAllowListReason != null
-//                        ? r.mInfoTempFgsAllowListReason.mCallingUid : INVALID_UID,
-//                r.mFgsNotificationWasDeferred,
-//                r.mFgsNotificationShown,
-//                0, // durationMs
-//                r.mStartForegroundCount,
-//                ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName),
-//                r.mFgsHasNotificationPermission,
-//                r.foregroundServiceType,
-//                0,
-//                r.mIsFgsDelegate,
-//                r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mClientUid : INVALID_UID,
-//                r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mDelegationService
-//                        : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT,
-//                apiState,
-//                apiType,
-//                timestamp);
+        FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
+                r.appInfo.uid,
+                r.shortInstanceName,
+                fgsState, // FGS State
+                r.mAllowWhileInUsePermissionInFgs, // allowWhileInUsePermissionInFgs
+                r.mAllowStartForeground, // fgsStartReasonCode
+                r.appInfo.targetSdkVersion,
+                r.mRecentCallingUid,
+                0, // callerTargetSdkVersion
+                r.mInfoTempFgsAllowListReason != null
+                        ? r.mInfoTempFgsAllowListReason.mCallingUid : INVALID_UID,
+                r.mFgsNotificationWasDeferred,
+                r.mFgsNotificationShown,
+                0, // durationMs
+                r.mStartForegroundCount,
+                ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName),
+                r.mFgsHasNotificationPermission,
+                r.foregroundServiceType,
+                0,
+                r.mIsFgsDelegate,
+                r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mClientUid : INVALID_UID,
+                r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mDelegationService
+                        : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT,
+                apiState,
+                apiType,
+                timestamp);
     }
 
     /**
@@ -467,31 +469,30 @@
     public void logFgsApiEventWithNoFgs(int uid,
             @FgsApiState int apiState,
             @ForegroundServiceApiType int[] apiType, long[] timestamp) {
-        // TODO: Uncomment when atom changes are in
-//        FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
-//                uid,
-//                null,
-//                FGS_STATE_CHANGED_API_CALL,
-//                false, // allowWhileInUsePermissionInFgs
-//                0, // fgsStartReasonCode
-//                0,
-//                uid,
-//                0, // callerTargetSdkVersion
-//                0,
-//                false,
-//                false,
-//                0, // durationMs
-//                0,
-//                0,
-//                false,
-//                0,
-//                0,
-//                false,
-//                0,
-//                0,
-//                apiState,
-//                apiType,
-//                timestamp);
+        FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
+                uid,
+                null,
+                FGS_STATE_CHANGED_API_CALL,
+                false, // allowWhileInUsePermissionInFgs
+                0, // fgsStartReasonCode
+                0,
+                uid,
+                0, // callerTargetSdkVersion
+                0,
+                false,
+                false,
+                0, // durationMs
+                0,
+                0,
+                false,
+                0,
+                0,
+                false,
+                0,
+                0,
+                apiState,
+                apiType,
+                timestamp);
     }
 
     /**
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index b001f3d..1268156 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1263,6 +1263,10 @@
         sendILMsg(MSG_IL_BTA2DP_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
     }
 
+    /*package*/ void setLeAudioTimeout(String address, int device, int delayMs) {
+        sendILMsg(MSG_IL_BTLEAUDIO_TIMEOUT, SENDMSG_QUEUE, device, address, delayMs);
+    }
+
     /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
         synchronized (mDeviceStateLock) {
             mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
@@ -1467,6 +1471,13 @@
                         mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
                     }
                     break;
+                case MSG_IL_BTLEAUDIO_TIMEOUT:
+                    // msg.obj  == address of LE Audio device
+                    synchronized (mDeviceStateLock) {
+                        mDeviceInventory.onMakeLeAudioDeviceUnavailableNow(
+                                (String) msg.obj, msg.arg1);
+                    }
+                    break;
                 case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
                     final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
                     synchronized (mDeviceStateLock) {
@@ -1556,6 +1567,7 @@
                 case MSG_I_BT_SERVICE_DISCONNECTED_PROFILE:
                     if (msg.arg1 != BluetoothProfile.HEADSET) {
                         synchronized (mDeviceStateLock) {
+                            mBtHelper.onBtProfileDisconnected(msg.arg1);
                             mDeviceInventory.onBtProfileDisconnected(msg.arg1);
                         }
                     } else {
@@ -1703,12 +1715,14 @@
 
     private static final int MSG_IL_SAVE_NDEF_DEVICE_FOR_STRATEGY = 47;
     private static final int MSG_IL_SAVE_REMOVE_NDEF_DEVICE_FOR_STRATEGY = 48;
+    private static final int MSG_IL_BTLEAUDIO_TIMEOUT = 49;
 
     private static boolean isMessageHandledUnderWakelock(int msgId) {
         switch(msgId) {
             case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
             case MSG_L_SET_BT_ACTIVE_DEVICE:
             case MSG_IL_BTA2DP_TIMEOUT:
+            case MSG_IL_BTLEAUDIO_TIMEOUT:
             case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
             case MSG_TOGGLE_HDMI:
             case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
@@ -1800,6 +1814,7 @@
                 case MSG_L_SET_BT_ACTIVE_DEVICE:
                 case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
                 case MSG_IL_BTA2DP_TIMEOUT:
+                case MSG_IL_BTLEAUDIO_TIMEOUT:
                 case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
                     if (sLastDeviceConnectMsgTime >= time) {
                         // add a little delay to make sure messages are ordered as expected
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index f9270c9..aae1d38 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -395,7 +395,7 @@
                 case BluetoothProfile.LE_AUDIO:
                 case BluetoothProfile.LE_AUDIO_BROADCAST:
                     if (switchToUnavailable) {
-                        makeLeAudioDeviceUnavailable(address, btInfo.mAudioSystemDevice);
+                        makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice);
                     } else if (switchToAvailable) {
                         makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
                                 streamType, btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10,
@@ -507,6 +507,12 @@
         }
     }
 
+    /*package*/ void onMakeLeAudioDeviceUnavailableNow(String address, int device) {
+        synchronized (mDevicesLock) {
+            makeLeAudioDeviceUnavailableNow(address, device);
+        }
+    }
+
     /*package*/ void onReportNewRoutes() {
         int n = mRoutesObservers.beginBroadcast();
         if (n > 0) {
@@ -1027,10 +1033,11 @@
             new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
                     .record();
             if (toRemove.size() > 0) {
-                final int delay = checkSendBecomingNoisyIntentInt(device, 0,
+                final int delay = checkSendBecomingNoisyIntentInt(device,
+                        AudioService.CONNECTION_STATE_DISCONNECTED,
                         AudioSystem.DEVICE_NONE);
                 toRemove.stream().forEach(deviceAddress ->
-                        makeLeAudioDeviceUnavailable(deviceAddress, device)
+                        makeLeAudioDeviceUnavailableLater(deviceAddress, device, delay)
                 );
             }
         }
@@ -1331,9 +1338,21 @@
              */
             mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
 
-            AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address, name),
+            final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+                    device, address, name),
                     AudioSystem.DEVICE_STATE_AVAILABLE,
                     AudioSystem.AUDIO_FORMAT_DEFAULT);
+            if (res != AudioSystem.AUDIO_STATUS_OK) {
+                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+                        "APM failed to make available LE Audio device addr=" + address
+                                + " error=" + res).printLog(TAG));
+                // TODO: connection failed, stop here
+                // TODO: return;
+            } else {
+                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+                        "LE Audio device addr=" + address + " now available").printLog(TAG));
+            }
+
             mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
                     new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
             mDeviceBroker.postAccessoryPlugMediaUnmute(device);
@@ -1354,11 +1373,23 @@
     }
 
     @GuardedBy("mDevicesLock")
-    private void makeLeAudioDeviceUnavailable(String address, int device) {
+    private void makeLeAudioDeviceUnavailableNow(String address, int device) {
         if (device != AudioSystem.DEVICE_NONE) {
-            AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address),
+            final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+                    device, address),
                     AudioSystem.DEVICE_STATE_UNAVAILABLE,
                     AudioSystem.AUDIO_FORMAT_DEFAULT);
+
+            if (res != AudioSystem.AUDIO_STATUS_OK) {
+                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+                        "APM failed to make unavailable LE Audio device addr=" + address
+                                + " error=" + res).printLog(TAG));
+                // TODO:  failed to disconnect, stop here
+                // TODO: return;
+            } else {
+                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+                        "LE Audio device addr=" + address + " made unavailable").printLog(TAG));
+            }
             mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
         }
 
@@ -1366,6 +1397,14 @@
     }
 
     @GuardedBy("mDevicesLock")
+    private void makeLeAudioDeviceUnavailableLater(String address, int device, int delayMs) {
+        // the device will be made unavailable later, so consider it disconnected right away
+        mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
+        // send the delayed message to make the device unavailable later
+        mDeviceBroker.setLeAudioTimeout(address, device, delayMs);
+    }
+
+    @GuardedBy("mDevicesLock")
     private void setCurrentAudioRouteNameIfPossible(String name, boolean fromA2dp) {
         synchronized (mCurAudioRoutes) {
             if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index df65dbd..2dcdc54 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -278,7 +278,11 @@
         }
         AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                 AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index));
-        mA2dp.setAvrcpAbsoluteVolume(index);
+        try {
+            mA2dp.setAvrcpAbsoluteVolume(index);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while changing abs volume", e);
+        }
     }
 
     /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getA2dpCodec(
@@ -286,7 +290,12 @@
         if (mA2dp == null) {
             return AudioSystem.AUDIO_FORMAT_DEFAULT;
         }
-        final BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device);
+        BluetoothCodecStatus btCodecStatus = null;
+        try {
+            btCodecStatus = mA2dp.getCodecStatus(device);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while getting status of " + device, e);
+        }
         if (btCodecStatus == null) {
             return AudioSystem.AUDIO_FORMAT_DEFAULT;
         }
@@ -420,7 +429,11 @@
         }
         AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                 AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, index, maxIndex));
-        mLeAudio.setVolume(volume);
+        try {
+            mLeAudio.setVolume(volume);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while setting LE volume", e);
+        }
     }
 
     /*package*/ synchronized void setHearingAidVolume(int index, int streamType,
@@ -446,7 +459,11 @@
             AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                     AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
         }
-        mHearingAid.setVolume(gainDB);
+        try {
+            mHearingAid.setVolume(gainDB);
+        } catch (Exception e) {
+            Log.i(TAG, "Exception while setting hearing aid volume", e);
+        }
     }
 
     /*package*/ synchronized void onBroadcastScoConnectionState(int state) {
@@ -471,7 +488,7 @@
     }
 
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void resetBluetoothSco() {
         mScoAudioState = SCO_STATE_INACTIVE;
         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -486,6 +503,35 @@
         mBluetoothHeadset = null;
     }
 
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    /*package*/ synchronized void onBtProfileDisconnected(int profile) {
+        switch (profile) {
+            case BluetoothProfile.A2DP:
+                mA2dp = null;
+                break;
+            case BluetoothProfile.HEARING_AID:
+                mHearingAid = null;
+                break;
+            case BluetoothProfile.LE_AUDIO:
+                mLeAudio = null;
+                break;
+
+            case BluetoothProfile.A2DP_SINK:
+            case BluetoothProfile.LE_AUDIO_BROADCAST:
+                // shouldn't be received here as profile doesn't involve BtHelper
+                Log.e(TAG, "onBtProfileDisconnected: Not a profile handled by BtHelper "
+                        + BluetoothProfile.getProfileName(profile));
+                break;
+
+            default:
+                // Not a valid profile to disconnect
+                Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect "
+                        + BluetoothProfile.getProfileName(profile));
+                break;
+        }
+    }
+
+    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
         if (profile == BluetoothProfile.HEADSET) {
             onHeadsetProfileConnected((BluetoothHeadset) proxy);
@@ -671,7 +717,6 @@
                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
                     switch(profile) {
                         case BluetoothProfile.A2DP:
-                        case BluetoothProfile.A2DP_SINK:
                         case BluetoothProfile.HEADSET:
                         case BluetoothProfile.HEARING_AID:
                         case BluetoothProfile.LE_AUDIO:
@@ -681,6 +726,10 @@
                             mDeviceBroker.postBtProfileConnected(profile, proxy);
                             break;
 
+                        case BluetoothProfile.A2DP_SINK:
+                            // no A2DP sink functionality handled by BtHelper
+                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            // no broadcast functionality handled by BtHelper
                         default:
                             break;
                     }
@@ -689,14 +738,19 @@
 
                     switch (profile) {
                         case BluetoothProfile.A2DP:
-                        case BluetoothProfile.A2DP_SINK:
                         case BluetoothProfile.HEADSET:
                         case BluetoothProfile.HEARING_AID:
                         case BluetoothProfile.LE_AUDIO:
-                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+                                    "BT profile service: disconnecting "
+                                        + BluetoothProfile.getProfileName(profile) + " profile"));
                             mDeviceBroker.postBtProfileDisconnected(profile);
                             break;
 
+                        case BluetoothProfile.A2DP_SINK:
+                            // no A2DP sink functionality handled by BtHelper
+                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            // no broadcast functionality handled by BtHelper
                         default:
                             break;
                     }
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 748c4ce5..4e8e704 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -108,6 +108,8 @@
     private static final String PERSIST_CSD_RECORD_SEPARATOR_CHAR = "|";
     private static final String PERSIST_CSD_RECORD_SEPARATOR = "\\|";
 
+    private static final long GLOBAL_TIME_OFFSET_UNINITIALIZED = -1;
+
     private final EventLogger mLogger = new EventLogger(AudioService.LOG_NB_EVENTS_SOUND_DOSE,
             "CSD updates");
 
@@ -168,6 +170,11 @@
     @GuardedBy("mCsdStateLock")
     private final List<SoundDoseRecord> mDoseRecords = new ArrayList<>();
 
+    // time in seconds reported by System.currentTimeInMillis used as an offset to convert between
+    // boot time and global time
+    @GuardedBy("mCsdStateLock")
+    private long mGlobalTimeOffsetInSecs = GLOBAL_TIME_OFFSET_UNINITIALIZED;
+
     private final Context mContext;
 
     private final ISoundDoseCallback.Stub mSoundDoseCallback = new ISoundDoseCallback.Stub() {
@@ -590,16 +597,24 @@
             Log.v(TAG, "Initializing sound dose");
 
             synchronized (mCsdStateLock) {
+                if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) {
+                    mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L;
+                }
+
+                float prevCsd = mCurrentCsd;
                 // Restore persisted values
                 mCurrentCsd = parseGlobalSettingFloat(
                         Settings.Global.AUDIO_SAFE_CSD_CURRENT_VALUE, /* defaultValue= */0.f);
-                mNextCsdWarning = parseGlobalSettingFloat(
-                        Settings.Global.AUDIO_SAFE_CSD_NEXT_WARNING, /* defaultValue= */1.f);
-                final List<SoundDoseRecord> records = persistedStringToRecordList(
-                        mSettings.getGlobalString(mAudioService.getContentResolver(),
-                                Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS));
-                if (records != null) {
-                    mDoseRecords.addAll(records);
+                if (mCurrentCsd != prevCsd) {
+                    mNextCsdWarning = parseGlobalSettingFloat(
+                            Settings.Global.AUDIO_SAFE_CSD_NEXT_WARNING, /* defaultValue= */1.f);
+                    final List<SoundDoseRecord> records = persistedStringToRecordList(
+                            mSettings.getGlobalString(mAudioService.getContentResolver(),
+                                    Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS),
+                            mGlobalTimeOffsetInSecs);
+                    if (records != null) {
+                        mDoseRecords.addAll(records);
+                    }
                 }
             }
 
@@ -774,8 +789,13 @@
         mLogger.enqueue(SoundDoseEvent.getDoseUpdateEvent(currentCsd, totalDuration));
     }
 
+    @SuppressWarnings("GuardedBy")  // avoid limitation with intra-procedural analysis of lambdas
     private void onPersistSoundDoseRecords() {
         synchronized (mCsdStateLock) {
+            if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) {
+                mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L;
+            }
+
             mSettings.putGlobalString(mAudioService.getContentResolver(),
                     Settings.Global.AUDIO_SAFE_CSD_CURRENT_VALUE,
                     Float.toString(mCurrentCsd));
@@ -785,28 +805,41 @@
             mSettings.putGlobalString(mAudioService.getContentResolver(),
                     Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS,
                     mDoseRecords.stream().map(
-                            SoundDoseHelper::recordToPersistedString).collect(
+                            record -> SoundDoseHelper.recordToPersistedString(record,
+                                    mGlobalTimeOffsetInSecs)).collect(
                             Collectors.joining(PERSIST_CSD_RECORD_SEPARATOR_CHAR)));
         }
     }
 
-    private static String recordToPersistedString(SoundDoseRecord record) {
-        return record.timestamp
+    private static String recordToPersistedString(SoundDoseRecord record,
+            long globalTimeOffsetInSecs) {
+        return convertToGlobalTime(record.timestamp, globalTimeOffsetInSecs)
                 + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.duration
                 + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.value
                 + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.averageMel;
     }
 
-    private static List<SoundDoseRecord> persistedStringToRecordList(String records) {
+    private static long convertToGlobalTime(long bootTimeInSecs, long globalTimeOffsetInSecs) {
+        return bootTimeInSecs + globalTimeOffsetInSecs;
+    }
+
+    private static long convertToBootTime(long globalTimeInSecs, long globalTimeOffsetInSecs) {
+        return globalTimeInSecs - globalTimeOffsetInSecs;
+    }
+
+    private static List<SoundDoseRecord> persistedStringToRecordList(String records,
+            long globalTimeOffsetInSecs) {
         if (records == null || records.isEmpty()) {
             return null;
         }
         return Arrays.stream(TextUtils.split(records, PERSIST_CSD_RECORD_SEPARATOR)).map(
-                SoundDoseHelper::persistedStringToRecord).filter(Objects::nonNull).collect(
+                record -> SoundDoseHelper.persistedStringToRecord(record,
+                        globalTimeOffsetInSecs)).filter(Objects::nonNull).collect(
                 Collectors.toList());
     }
 
-    private static SoundDoseRecord persistedStringToRecord(String record) {
+    private static SoundDoseRecord persistedStringToRecord(String record,
+            long globalTimeOffsetInSecs) {
         if (record == null || record.isEmpty()) {
             return null;
         }
@@ -818,7 +851,8 @@
 
         final SoundDoseRecord sdRecord = new SoundDoseRecord();
         try {
-            sdRecord.timestamp = Long.parseLong(fields[0]);
+            sdRecord.timestamp = convertToBootTime(Long.parseLong(fields[0]),
+                    globalTimeOffsetInSecs);
             sdRecord.duration = Integer.parseInt(fields[1]);
             sdRecord.value = Float.parseFloat(fields[2]);
             sdRecord.averageMel = Float.parseFloat(fields[3]);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 850f6da..81e550e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -466,6 +466,8 @@
     private boolean mIsDocked;
     private boolean mIsDreaming;
 
+    private boolean mBootCompleted = false;
+
     private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -602,6 +604,12 @@
                 }
             }
         } else if (phase == PHASE_BOOT_COMPLETED) {
+            synchronized (mSyncRoot) {
+                mBootCompleted = true;
+                for (int i = 0; i < mDisplayPowerControllers.size(); i++) {
+                    mDisplayPowerControllers.valueAt(i).onBootCompleted();
+                }
+            }
             mDisplayModeDirector.onBootCompleted();
             mLogicalDisplayMapper.onBootCompleted();
         }
@@ -2998,12 +3006,12 @@
             displayPowerController = new DisplayPowerController2(
                     mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                     mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display), hbmMetadata);
+                    () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
         } else {
             displayPowerController = new DisplayPowerController(
                     mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                     mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display), hbmMetadata);
+                    () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
         }
         mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 82e6e30..40c879a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -139,6 +139,7 @@
     private static final int MSG_BRIGHTNESS_RAMP_DONE = 12;
     private static final int MSG_STATSD_HBM_BRIGHTNESS = 13;
     private static final int MSG_SWITCH_USER = 14;
+    private static final int MSG_BOOT_COMPLETED = 15;
 
     private static final int PROXIMITY_UNKNOWN = -1;
     private static final int PROXIMITY_NEGATIVE = 0;
@@ -518,6 +519,8 @@
     private final SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
             new SparseArray<>();
 
+    private boolean mBootCompleted;
+
     /**
      * Creates the display power controller.
      */
@@ -525,7 +528,8 @@
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
-            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
+            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
+            boolean bootCompleted) {
 
         mInjector = injector != null ? injector : new Injector();
         mClock = mInjector.getClock();
@@ -662,6 +666,7 @@
         mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
+        mBootCompleted = bootCompleted;
     }
 
     private void applyReduceBrightColorsSplineAdjustment() {
@@ -1448,7 +1453,7 @@
 
         // Initialize things the first time the power state is changed.
         if (mustInitialize) {
-            initialize(state);
+            initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
         }
 
         // Animate the screen state change unless already animating.
@@ -2154,7 +2159,8 @@
                 }
             }
 
-            if (!reportOnly && mPowerState.getScreenState() != state) {
+            if (!reportOnly && mPowerState.getScreenState() != state
+                    && readyToUpdateDisplayState()) {
                 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
                 // TODO(b/153319140) remove when we can get this from the above trace invocation
                 SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
@@ -2567,6 +2573,12 @@
         mBrightnessSetting.setBrightness(brightnessValue);
     }
 
+    @Override
+    public void onBootCompleted() {
+        Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED);
+        mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+    }
+
     private void updateScreenBrightnessSetting(float brightnessValue) {
         if (!isValidBrightnessValue(brightnessValue)
                 || brightnessValue == mCurrentScreenBrightnessSetting) {
@@ -2712,6 +2724,17 @@
         }
     };
 
+    /**
+     * Indicates whether the display state is ready to update. If this is the default display, we
+     * want to update it right away so that we can draw the boot animation on it. If it is not
+     * the default display, drawing the boot animation on it would look incorrect, so we need
+     * to wait until boot is completed.
+     * @return True if the display state is ready to update
+     */
+    private boolean readyToUpdateDisplayState() {
+        return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted;
+    }
+
     @Override
     public void dump(final PrintWriter pw) {
         synchronized (mLock) {
@@ -3235,6 +3258,11 @@
                 case MSG_SWITCH_USER:
                     handleOnSwitchUser(msg.arg1);
                     break;
+
+                case MSG_BOOT_COMPLETED:
+                    mBootCompleted = true;
+                    updatePowerState();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 0861a37..5ea8aaa 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -137,6 +137,7 @@
     private static final int MSG_BRIGHTNESS_RAMP_DONE = 10;
     private static final int MSG_STATSD_HBM_BRIGHTNESS = 11;
     private static final int MSG_SWITCH_USER = 12;
+    private static final int MSG_BOOT_COMPLETED = 13;
 
     private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
 
@@ -425,6 +426,8 @@
     private SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
             new SparseArray();
 
+    private boolean mBootCompleted;
+
     /**
      * Creates the display power controller.
      */
@@ -432,7 +435,8 @@
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
-            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
+            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
+            boolean bootCompleted) {
 
         mInjector = injector != null ? injector : new Injector();
         mClock = mInjector.getClock();
@@ -555,6 +559,7 @@
         mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
+        mBootCompleted = bootCompleted;
     }
 
     private void applyReduceBrightColorsSplineAdjustment() {
@@ -1190,7 +1195,7 @@
 
         // Initialize things the first time the power state is changed.
         if (mustInitialize) {
-            initialize(state);
+            initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
         }
 
         // Animate the screen state change unless already animating.
@@ -1720,6 +1725,12 @@
         }
     }
 
+    @Override
+    public void onBootCompleted() {
+        Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED);
+        mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+    }
+
     private boolean saveBrightnessInfo(float brightness) {
         return saveBrightnessInfo(brightness, brightness);
     }
@@ -1861,7 +1872,8 @@
                 }
             }
 
-            if (!reportOnly && mPowerState.getScreenState() != state) {
+            if (!reportOnly && mPowerState.getScreenState() != state
+                    && readyToUpdateDisplayState()) {
                 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
                 // TODO(b/153319140) remove when we can get this from the above trace invocation
                 SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
@@ -2519,6 +2531,17 @@
         }
     }
 
+    /**
+     * Indicates whether the display state is ready to update. If this is the default display, we
+     * want to update it right away so that we can draw the boot animation on it. If it is not
+     * the default display, drawing the boot animation on it would look incorrect, so we need
+     * to wait until boot is completed.
+     * @return True if the display state is ready to update
+     */
+    private boolean readyToUpdateDisplayState() {
+        return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted;
+    }
+
     // Return bucket index of range_[left]_[right] where
     // left <= nits < right
     private int nitsToRangeIndex(float nits) {
@@ -2738,6 +2761,11 @@
                 case MSG_SWITCH_USER:
                     handleOnSwitchUser(msg.arg1);
                     break;
+
+                case MSG_BOOT_COMPLETED:
+                    mBootCompleted = true;
+                    updatePowerState();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index 0bc8154..73edb97 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -206,4 +206,9 @@
      * @param follower The DPC to remove from the followers list
      */
     void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower);
+
+    /**
+     * Indicate that boot has been completed and the screen is ready to update.
+     */
+    void onBootCompleted();
 }
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index a9b9b54..4d134b6 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.media;
 
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -248,12 +250,17 @@
 
     // Binder call
     @Override
-    public void showMediaOutputSwitcher(String packageName) {
+    public boolean showMediaOutputSwitcher(String packageName) {
         if (!validatePackageName(Binder.getCallingUid(), packageName)) {
             throw new SecurityException("packageName must match the calling identity");
         }
         final long token = Binder.clearCallingIdentity();
         try {
+            if (mContext.getSystemService(ActivityManager.class).getPackageImportance(packageName)
+                    > IMPORTANCE_FOREGROUND) {
+                Slog.w(TAG, "showMediaOutputSwitcher only works when called from foreground");
+                return false;
+            }
             synchronized (mLock) {
                 StatusBarManagerInternal statusBar =
                         LocalServices.getService(StatusBarManagerInternal.class);
@@ -262,6 +269,7 @@
         } finally {
             Binder.restoreCallingIdentity(token);
         }
+        return true;
     }
 
     // Binder call
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bba1dbe..a01b2c1 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8695,6 +8695,9 @@
             if (interceptBefore && !record.isIntercepted()
                     && record.isNewEnoughForAlerting(System.currentTimeMillis())) {
                 buzzBeepBlinkLocked(record);
+
+                // Log alert after change in intercepted state to Zen Log as well
+                ZenLog.traceAlertOnUpdatedIntercept(record);
             }
         }
         if (changed) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 2ea6c40..1cfcb4e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -114,6 +114,8 @@
 
     // is this notification currently being intercepted by Zen Mode?
     private boolean mIntercept;
+    // has the intercept value been set explicitly? we only want to log it if new or changed
+    private boolean mInterceptSet;
 
     // is this notification hidden since the app pkg is suspended?
     private boolean mHidden;
@@ -929,6 +931,7 @@
 
     public boolean setIntercepted(boolean intercept) {
         mIntercept = intercept;
+        mInterceptSet = true;
         return mIntercept;
     }
 
@@ -949,6 +952,10 @@
         return mIntercept;
     }
 
+    public boolean hasInterceptBeenSet() {
+        return mInterceptSet;
+    }
+
     public boolean isNewEnoughForAlerting(long now) {
         return getFreshnessMs(now) <= MAX_SOUND_DELAY_MS;
     }
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index c0bc474..35b94e7 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -68,20 +68,23 @@
     private static final int TYPE_MATCHES_CALL_FILTER = 18;
     private static final int TYPE_RECORD_CALLER = 19;
     private static final int TYPE_CHECK_REPEAT_CALLER = 20;
+    private static final int TYPE_ALERT_ON_UPDATED_INTERCEPT = 21;
 
     private static int sNext;
     private static int sSize;
 
     public static void traceIntercepted(NotificationRecord record, String reason) {
-        if (record != null && record.isIntercepted()) return;  // already logged
         append(TYPE_INTERCEPTED, record.getKey() + "," + reason);
     }
 
     public static void traceNotIntercepted(NotificationRecord record, String reason) {
-        if (record != null && record.isUpdate) return;  // already logged
         append(TYPE_NOT_INTERCEPTED, record.getKey() + "," + reason);
     }
 
+    public static void traceAlertOnUpdatedIntercept(NotificationRecord record) {
+        append(TYPE_ALERT_ON_UPDATED_INTERCEPT, record.getKey());
+    }
+
     public static void traceSetRingerModeExternal(int ringerModeOld, int ringerModeNew,
             String caller, int ringerModeInternalIn, int ringerModeInternalOut) {
         append(TYPE_SET_RINGER_MODE_EXTERNAL, caller + ",e:" +
@@ -219,6 +222,7 @@
             case TYPE_MATCHES_CALL_FILTER: return "matches_call_filter";
             case TYPE_RECORD_CALLER: return "record_caller";
             case TYPE_CHECK_REPEAT_CALLER: return "check_repeat_caller";
+            case TYPE_ALERT_ON_UPDATED_INTERCEPT: return "alert_on_updated_intercept";
             default: return "unknown";
         }
     }
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index db0ce2e..5b7b0c1 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -155,85 +155,85 @@
 
         if (isCritical(record)) {
             // Zen mode is ignored for critical notifications.
-            ZenLog.traceNotIntercepted(record, "criticalNotification");
+            maybeLogInterceptDecision(record, false, "criticalNotification");
             return false;
         }
         // Make an exception to policy for the notification saying that policy has changed
         if (NotificationManager.Policy.areAllVisualEffectsSuppressed(policy.suppressedVisualEffects)
                 && "android".equals(record.getSbn().getPackageName())
                 && SystemMessageProto.SystemMessage.NOTE_ZEN_UPGRADE == record.getSbn().getId()) {
-            ZenLog.traceNotIntercepted(record, "systemDndChangedNotification");
+            maybeLogInterceptDecision(record, false, "systemDndChangedNotification");
             return false;
         }
         switch (zen) {
             case Global.ZEN_MODE_NO_INTERRUPTIONS:
                 // #notevenalarms
-                ZenLog.traceIntercepted(record, "none");
+                maybeLogInterceptDecision(record, true, "none");
                 return true;
             case Global.ZEN_MODE_ALARMS:
                 if (isAlarm(record)) {
                     // Alarms only
-                    ZenLog.traceNotIntercepted(record, "alarm");
+                    maybeLogInterceptDecision(record, false, "alarm");
                     return false;
                 }
-                ZenLog.traceIntercepted(record, "alarmsOnly");
+                maybeLogInterceptDecision(record, true, "alarmsOnly");
                 return true;
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                 // allow user-prioritized packages through in priority mode
                 if (record.getPackagePriority() == Notification.PRIORITY_MAX) {
-                    ZenLog.traceNotIntercepted(record, "priorityApp");
+                    maybeLogInterceptDecision(record, false, "priorityApp");
                     return false;
                 }
 
                 if (isAlarm(record)) {
                     if (!policy.allowAlarms()) {
-                        ZenLog.traceIntercepted(record, "!allowAlarms");
+                        maybeLogInterceptDecision(record, true, "!allowAlarms");
                         return true;
                     }
-                    ZenLog.traceNotIntercepted(record, "allowedAlarm");
+                    maybeLogInterceptDecision(record, false, "allowedAlarm");
                     return false;
                 }
                 if (isEvent(record)) {
                     if (!policy.allowEvents()) {
-                        ZenLog.traceIntercepted(record, "!allowEvents");
+                        maybeLogInterceptDecision(record, true, "!allowEvents");
                         return true;
                     }
-                    ZenLog.traceNotIntercepted(record, "allowedEvent");
+                    maybeLogInterceptDecision(record, false, "allowedEvent");
                     return false;
                 }
                 if (isReminder(record)) {
                     if (!policy.allowReminders()) {
-                        ZenLog.traceIntercepted(record, "!allowReminders");
+                        maybeLogInterceptDecision(record, true, "!allowReminders");
                         return true;
                     }
-                    ZenLog.traceNotIntercepted(record, "allowedReminder");
+                    maybeLogInterceptDecision(record, false, "allowedReminder");
                     return false;
                 }
                 if (isMedia(record)) {
                     if (!policy.allowMedia()) {
-                        ZenLog.traceIntercepted(record, "!allowMedia");
+                        maybeLogInterceptDecision(record, true, "!allowMedia");
                         return true;
                     }
-                    ZenLog.traceNotIntercepted(record, "allowedMedia");
+                    maybeLogInterceptDecision(record, false, "allowedMedia");
                     return false;
                 }
                 if (isSystem(record)) {
                     if (!policy.allowSystem()) {
-                        ZenLog.traceIntercepted(record, "!allowSystem");
+                        maybeLogInterceptDecision(record, true, "!allowSystem");
                         return true;
                     }
-                    ZenLog.traceNotIntercepted(record, "allowedSystem");
+                    maybeLogInterceptDecision(record, false, "allowedSystem");
                     return false;
                 }
                 if (isConversation(record)) {
                     if (policy.allowConversations()) {
                         if (policy.priorityConversationSenders == CONVERSATION_SENDERS_ANYONE) {
-                            ZenLog.traceNotIntercepted(record, "conversationAnyone");
+                            maybeLogInterceptDecision(record, false, "conversationAnyone");
                             return false;
                         } else if (policy.priorityConversationSenders
                                 == NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT
                                 && record.getChannel().isImportantConversation()) {
-                            ZenLog.traceNotIntercepted(record, "conversationMatches");
+                            maybeLogInterceptDecision(record, false, "conversationMatches");
                             return false;
                         }
                     }
@@ -244,31 +244,59 @@
                     if (policy.allowRepeatCallers()
                             && REPEAT_CALLERS.isRepeat(
                                     mContext, extras(record), record.getPhoneNumbers())) {
-                        ZenLog.traceNotIntercepted(record, "repeatCaller");
+                        maybeLogInterceptDecision(record, false, "repeatCaller");
                         return false;
                     }
                     if (!policy.allowCalls()) {
-                        ZenLog.traceIntercepted(record, "!allowCalls");
+                        maybeLogInterceptDecision(record, true, "!allowCalls");
                         return true;
                     }
                     return shouldInterceptAudience(policy.allowCallsFrom(), record);
                 }
                 if (isMessage(record)) {
                     if (!policy.allowMessages()) {
-                        ZenLog.traceIntercepted(record, "!allowMessages");
+                        maybeLogInterceptDecision(record, true, "!allowMessages");
                         return true;
                     }
                     return shouldInterceptAudience(policy.allowMessagesFrom(), record);
                 }
 
-                ZenLog.traceIntercepted(record, "!priority");
+                maybeLogInterceptDecision(record, true, "!priority");
                 return true;
             default:
-                ZenLog.traceNotIntercepted(record, "unknownZenMode");
+                maybeLogInterceptDecision(record, false, "unknownZenMode");
                 return false;
         }
     }
 
+    // Consider logging the decision of shouldIntercept for the given record.
+    // This will log the outcome if one of the following is true:
+    //   - it's the first time the intercept decision is set for the record
+    //   - OR it's not the first time, but the intercept decision changed
+    private static void maybeLogInterceptDecision(NotificationRecord record, boolean intercept,
+            String reason) {
+        boolean interceptBefore = record.isIntercepted();
+        if (record.hasInterceptBeenSet() && (interceptBefore == intercept)) {
+            // this record has already been evaluated for whether it should be intercepted, and
+            // the decision has not changed.
+            return;
+        }
+
+        // add a note to the reason indicating whether it's new or updated
+        String annotatedReason = reason;
+        if (!record.hasInterceptBeenSet()) {
+            annotatedReason = "new:" + reason;
+        } else if (interceptBefore != intercept) {
+            annotatedReason = "updated:" + reason;
+        }
+
+        if (intercept) {
+            ZenLog.traceIntercepted(record, annotatedReason);
+        } else {
+            ZenLog.traceNotIntercepted(record, annotatedReason);
+        }
+    }
+
     /**
      * Check if the notification is too critical to be suppressed.
      *
@@ -285,10 +313,10 @@
     private static boolean shouldInterceptAudience(int source, NotificationRecord record) {
         float affinity = record.getContactAffinity();
         if (!audienceMatches(source, affinity)) {
-            ZenLog.traceIntercepted(record, "!audienceMatches,affinity=" + affinity);
+            maybeLogInterceptDecision(record, true, "!audienceMatches,affinity=" + affinity);
             return true;
         }
-        ZenLog.traceNotIntercepted(record, "affinity=" + affinity);
+        maybeLogInterceptDecision(record, false, "affinity=" + affinity);
         return false;
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 7a95987..01e9522 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1365,6 +1365,20 @@
         }
     }
 
+    public void setAllowCrossUidActivitySwitchFromBelow(IBinder token, boolean allowed) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
+                if (r != null) {
+                    r.setAllowCrossUidActivitySwitchFromBelow(allowed);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
     @Override
     public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) {
         final long origId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 828848b..89b7968 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -314,6 +314,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.MergedConfiguration;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -701,6 +702,11 @@
     private boolean mInheritShownWhenLocked;
     private boolean mTurnScreenOn;
 
+    /** Allow activity launches which would otherwise be blocked by
+     * {@link ActivityTransitionSecurityController#checkActivityAllowedToStart}
+     */
+    private boolean mAllowCrossUidActivitySwitchFromBelow;
+
     /** Have we been asked to have this token keep the screen frozen? */
     private boolean mFreezingScreen;
 
@@ -2153,7 +2159,7 @@
 
         boolean appActivityEmbeddingEnabled = false;
         try {
-            appActivityEmbeddingEnabled = WindowManagerService.sWindowExtensionsEnabled
+            appActivityEmbeddingEnabled = WindowManager.hasWindowExtensionsEnabled()
                     && mAtmService.mContext.getPackageManager()
                             .getProperty(PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED, packageName)
                             .getBoolean();
@@ -9836,6 +9842,54 @@
         mTurnScreenOn = turnScreenOn;
     }
 
+    void setAllowCrossUidActivitySwitchFromBelow(boolean allowed) {
+        mAllowCrossUidActivitySwitchFromBelow = allowed;
+    }
+
+    /**
+     * Determines if a source is allowed to add or remove activities from the task,
+     * if the current ActivityRecord is above it in the stack
+     *
+     * A transition is blocked ({@code false} returned) if all of the following are met:
+     * <pre>
+     * 1. The source activity and the current activity record belong to different apps
+     * (i.e, have different UIDs).
+     * 2. Both the source activity and the current activity target U+
+     * 3. The current activity has not set
+     * {@link ActivityRecord#setAllowCrossUidActivitySwitchFromBelow(boolean)} to {@code true}
+     * </pre>
+     *
+     * Returns a pair where the elements mean:
+     * <pre>
+     * First: {@code false} if we should actually block the transition (takes into consideration
+     * feature flag and targetSdk).
+     * Second: {@code false} if we should warn about the transition via toasts. This happens if
+     * the transition would be blocked in case both the app was targeting U+ and the feature was
+     * enabled.
+     * </pre>
+     *
+     * @param sourceUid The source (s) activity performing the state change
+     */
+    Pair<Boolean, Boolean> allowCrossUidActivitySwitchFromBelow(int sourceUid) {
+        int myUid = info.applicationInfo.uid;
+        if (sourceUid == myUid) {
+            return new Pair<>(true, true);
+        }
+
+        // If mAllowCrossUidActivitySwitchFromBelow is set, honor it.
+        if (mAllowCrossUidActivitySwitchFromBelow) {
+            return new Pair<>(true, true);
+        }
+
+        // If it is not set, default to true if both records target ≥ U, false otherwise
+        // TODO(b/258792202) Replace with CompatChanges and replace Pair with boolean once feature
+        // flag is removed
+        boolean restrictActivitySwitch =
+                ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(myUid)
+                    && ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(sourceUid);
+        return new Pair<>(!restrictActivitySwitch, false);
+    }
+
     boolean getTurnScreenOnFlag() {
         return mTurnScreenOn || containsTurnScreenOnWindow();
     }
@@ -9944,7 +9998,8 @@
         super.dumpDebug(proto, WINDOW_TOKEN, logLevel);
         proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing);
         proto.write(IS_WAITING_FOR_TRANSITION_START, isWaitingForTransitionStart());
-        proto.write(IS_ANIMATING, isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION));
+        proto.write(IS_ANIMATING, isAnimating(TRANSITION | PARENTS | CHILDREN,
+                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION));
         if (mThumbnail != null){
             mThumbnail.dumpDebug(proto, THUMBNAIL);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 1d5fcd7..32dac49 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -125,6 +125,7 @@
 import android.os.UserManager;
 import android.service.voice.IVoiceInteractionSession;
 import android.text.TextUtils;
+import android.util.Pair;
 import android.util.Pools.SynchronizedPool;
 import android.util.Slog;
 import android.widget.Toast;
@@ -1908,36 +1909,21 @@
      * Log activity starts which violate one of the following rules of the
      * activity security model (ASM):
      * See go/activity-security for rationale behind the rules.
-     * We don't currently block, but these checks may later become blocks
      * 1. Within a task, only an activity matching a top UID of the task can start activities
      * 2. Only activities within a foreground task, which match a top UID of the task, can
      * create a new task or bring an existing one into the foreground
      */
     private boolean checkActivitySecurityModel(ActivityRecord r, boolean newTask, Task targetTask) {
+        // BAL Exception allowed in all cases
+        if (mBalCode == BAL_ALLOW_ALLOWLISTED_UID) {
+            return true;
+        }
+
         // Intents with FLAG_ACTIVITY_NEW_TASK will always be considered as creating a new task
         // even if the intent is delivered to an existing task.
         boolean taskToFront = newTask
                 || (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == FLAG_ACTIVITY_NEW_TASK;
 
-        if (mSourceRecord != null) {
-            boolean passesAsmChecks = true;
-            Task sourceTask = mSourceRecord.getTask();
-
-            // Don't allow launches into a new task if the current task is not foreground.
-            if (taskToFront) {
-                passesAsmChecks = sourceTask != null && sourceTask.isVisible();
-            }
-
-            Task taskToCheck = taskToFront ? sourceTask : targetTask;
-            passesAsmChecks = passesAsmChecks && ActivityTaskSupervisor
-                    .doesTopActivityMatchingUidExistForAsm(taskToCheck, mSourceRecord.getUid(),
-                            mSourceRecord);
-
-            if (passesAsmChecks) {
-                return true;
-            }
-        }
-
         // BAL exception only allowed for new tasks
         if (taskToFront) {
             if (mBalCode == BAL_ALLOW_ALLOWLISTED_COMPONENT
@@ -1949,9 +1935,34 @@
             }
         }
 
-        // BAL Exception allowed in all cases
-        if (mBalCode == BAL_ALLOW_ALLOWLISTED_UID) {
-            return true;
+        boolean shouldBlockActivityStart = true;
+        // Used for logging/toasts. Would we block the start if target sdk was U and feature was
+        // enabled?
+        boolean wouldBlockActivityStartIgnoringFlags = true;
+
+        if (mSourceRecord != null) {
+            boolean passesAsmChecks = true;
+            Task sourceTask = mSourceRecord.getTask();
+
+            // Don't allow launches into a new task if the current task is not foreground.
+            if (taskToFront) {
+                passesAsmChecks = sourceTask != null && sourceTask.isVisible();
+            }
+
+            if (passesAsmChecks) {
+                Task taskToCheck = taskToFront ? sourceTask : targetTask;
+                // first == false means Should Block
+                // second == false means Would Block disregarding flags
+                Pair<Boolean, Boolean> pair = ActivityTaskSupervisor
+                        .doesTopActivityMatchingUidExistForAsm(taskToCheck, mSourceRecord.getUid(),
+                                mSourceRecord);
+                shouldBlockActivityStart = !pair.first;
+                wouldBlockActivityStartIgnoringFlags = !pair.second;
+            }
+
+            if (!wouldBlockActivityStartIgnoringFlags) {
+                return true;
+            }
         }
 
         // ASM rules have failed. Log why
@@ -1996,19 +2007,20 @@
                 mBalCode
         );
 
-        boolean shouldBlockActivityStart =
-                ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(mCallingUid);
+        boolean blockActivityStartAndFeatureEnabled = ActivitySecurityModelFeatureFlags
+                    .shouldRestrictActivitySwitch(mCallingUid)
+                && shouldBlockActivityStart;
 
         if (ActivitySecurityModelFeatureFlags.shouldShowToast(mCallingUid)) {
             UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
                     "Activity start from " + r.launchedFromPackage
-                            + (shouldBlockActivityStart ? " " : " would be ")
+                            + (blockActivityStartAndFeatureEnabled ? " " : " would be ")
                             + "blocked by " + ActivitySecurityModelFeatureFlags.DOC_LINK,
                     Toast.LENGTH_SHORT).show());
         }
 
 
-        if (shouldBlockActivityStart) {
+        if (blockActivityStartAndFeatureEnabled) {
             Slog.e(TAG, "Abort Launching r: " + r
                     + " as source: "
                     + (mSourceRecord != null ? mSourceRecord : r.launchedFromPackage)
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 3876290..ef47b6e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -133,6 +133,7 @@
 import android.provider.MediaStore;
 import android.util.ArrayMap;
 import android.util.MergedConfiguration;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -1635,14 +1636,18 @@
             // Prevent recursion.
             return;
         }
-        boolean passesAsmChecks = true;
+        boolean shouldBlockActivitySwitchIfFeatureEnabled = false;
+        boolean wouldBlockActivitySwitchIgnoringFlags = false;
         // We may have already checked that the callingUid has additional clearTask privileges, and
         // cleared the calling identify. If so, we infer we do not need further restrictions here.
         // TODO(b/263368846) Move to live with the rest of the ASM logic.
         if (callingUid != SYSTEM_UID) {
-            passesAsmChecks = doesTopActivityMatchingUidExistForAsm(task, callingUid,
+            Pair<Boolean, Boolean> pair = doesTopActivityMatchingUidExistForAsm(task,
+                    callingUid,
                     null);
-            if (!passesAsmChecks) {
+            shouldBlockActivitySwitchIfFeatureEnabled = !pair.first;
+            wouldBlockActivitySwitchIgnoringFlags = !pair.second;
+            if (wouldBlockActivitySwitchIgnoringFlags) {
                 ActivityRecord topActivity =  task.getActivity(ar ->
                         !ar.isState(FINISHING) && !ar.isAlwaysOnTop());
                 FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
@@ -1685,13 +1690,13 @@
             if (task.isPersistable) {
                 mService.notifyTaskPersisterLocked(null, true);
             }
-            if (!passesAsmChecks) {
-                boolean shouldRestrictActivitySwitch =
-                        ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(callingUid);
-
+            if (wouldBlockActivitySwitchIgnoringFlags) {
+                boolean restrictActivitySwitch = ActivitySecurityModelFeatureFlags
+                        .shouldRestrictActivitySwitch(callingUid)
+                        && shouldBlockActivitySwitchIfFeatureEnabled;
                 if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) {
                     UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
-                            (shouldRestrictActivitySwitch
+                            (restrictActivitySwitch
                                     ? "Returning home due to "
                                     : "Would return home due to ")
                                     + ActivitySecurityModelFeatureFlags.DOC_LINK,
@@ -1701,7 +1706,7 @@
                 // If the activity switch should be restricted, return home rather than the
                 // previously top task, to prevent users from being confused which app they're
                 // viewing
-                if (shouldRestrictActivitySwitch) {
+                if (restrictActivitySwitch) {
                     Slog.w(TAG, "Return to home as source uid: " + callingUid
                             + "is not on top of task t: " + task);
                     task.getTaskDisplayArea().moveHomeActivityToTop("taskRemoved");
@@ -1721,14 +1726,18 @@
      *
      *  The 'sourceRecord' can be considered top even if it is 'finishing'
      *
-     *  TODO(b/263368846) Shift to BackgroundActivityStartController once class is ready
+     * @return A pair where the first value is the return value matching the checks above, and the
+     * second value is the return value disregarding the feature flag or target api levels. Use the
+     * first value for blocking launches - the second value is only used to determine if a toast
+     * should be displayed, and will be used alongside a feature flag in {@link ActivityStarter}.
      */
+    // TODO(b/263368846) Shift to BackgroundActivityStartController once class is ready
     @Nullable
-    static boolean doesTopActivityMatchingUidExistForAsm(@Nullable Task task,
+    static Pair<Boolean, Boolean> doesTopActivityMatchingUidExistForAsm(@Nullable Task task,
             int uid, @Nullable ActivityRecord sourceRecord) {
         // If the source is visible, consider it 'top'.
         if (sourceRecord != null && sourceRecord.isVisible()) {
-            return true;
+            return new Pair<>(true, true);
         }
 
         // Consider the source activity, whether or not it is finishing. Do not consider any other
@@ -1739,28 +1748,33 @@
         // Check top of stack (or the first task fragment for embedding).
         ActivityRecord topActivity = task.getActivity(topOfStackPredicate);
         if (topActivity == null) {
-            return false;
+            return new Pair<>(false, false);
         }
 
-        if (topActivity.getUid() == uid) {
-            return true;
+        Pair<Boolean, Boolean> pair = topActivity.allowCrossUidActivitySwitchFromBelow(uid);
+        if (pair.first) {
+            return new Pair<>(true, pair.second);
         }
 
         // Even if the top activity is not a match, we may be in an embedded activity scenario with
         // an adjacent task fragment. Get the second fragment.
         TaskFragment taskFragment = topActivity.getTaskFragment();
         if (taskFragment == null) {
-            return false;
+            return new Pair<>(false, false);
         }
 
         TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
         if (adjacentTaskFragment == null) {
-            return false;
+            return new Pair<>(false, false);
         }
 
         // Check the second fragment.
         topActivity = adjacentTaskFragment.getActivity(topOfStackPredicate);
-        return topActivity != null && topActivity.getUid() == uid;
+        if (topActivity == null) {
+            return new Pair<>(false, false);
+        }
+
+        return topActivity.allowCrossUidActivitySwitchFromBelow(uid);
     }
 
     void cleanUpRemovedTaskLocked(Task task, boolean killProcess, boolean removeFromRecents) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d5bf9f9..c30aef4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -442,16 +442,6 @@
     static final boolean ENABLE_FIXED_ROTATION_TRANSFORM =
             SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true);
 
-    /**
-     * Whether the device supports the WindowManager Extensions.
-     * OEMs can enable this by having their device config to inherit window_extensions.mk, such as:
-     * <pre>
-     * $(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)
-     * </pre>
-     */
-    static final boolean sWindowExtensionsEnabled =
-            SystemProperties.getBoolean("persist.wm.extensions.enabled", false);
-
     // Enums for animation scale update types.
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a23f889..684ede3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4550,7 +4550,13 @@
     @Override
     public boolean addCrossProfileWidgetProvider(ComponentName admin, String callerPackageName,
             String packageName) {
-        final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+        CallerIdentity caller;
+
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(admin, callerPackageName);
+        } else {
+            caller = getCallerIdentity(admin);
+        }
         ActiveAdmin activeAdmin;
 
         if (isPermissionCheckFlagEnabled()) {
@@ -4598,7 +4604,13 @@
     @Override
     public boolean removeCrossProfileWidgetProvider(ComponentName admin, String callerPackageName,
             String packageName) {
-        final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(admin, callerPackageName);
+        } else {
+            caller = getCallerIdentity(admin);
+        }
+
         ActiveAdmin activeAdmin;
 
         if (isPermissionCheckFlagEnabled()) {
@@ -4646,7 +4658,12 @@
     @Override
     public List<String> getCrossProfileWidgetProviders(ComponentName admin,
             String callerPackageName) {
-        final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(admin, callerPackageName);
+        } else {
+            caller = getCallerIdentity(admin);
+        }
         ActiveAdmin activeAdmin;
 
         if (isPermissionCheckFlagEnabled()) {
@@ -7367,7 +7384,12 @@
         if (!mHasFeature && !hasCallingOrSelfPermission(permission.MASTER_CLEAR)) {
             return;
         }
-        final CallerIdentity caller = getCallerIdentity(callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(callerPackageName);
+        }  else {
+            caller = getCallerIdentity();
+        }
         ActiveAdmin admin;
 
         boolean calledByProfileOwnerOnOrgOwnedDevice =
@@ -8647,7 +8669,12 @@
             return;
         }
 
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
 
         if (isPermissionCheckFlagEnabled()) {
             // The effect of this policy is device-wide.
@@ -8676,8 +8703,12 @@
         if (!mHasFeature) {
             return false;
         }
-
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
 
         if (isPermissionCheckFlagEnabled()) {
             enforceCanQuery(caller.getPackageName(), SET_TIME, UserHandle.USER_ALL);
@@ -8701,7 +8732,12 @@
             return;
         }
 
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
 
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
             // The effect of this policy is device-wide.
@@ -8742,7 +8778,12 @@
             return false;
         }
 
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
 
         if (isPermissionCheckFlagEnabled()) {
             // The effect of this policy is device-wide.
@@ -8966,7 +9007,12 @@
         if (!mHasFeature) {
             return false;
         }
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         if (isPermissionCheckFlagEnabled()) {
             Preconditions.checkCallAuthorization(
                     hasFullCrossUsersPermission(caller, userHandle)
@@ -10899,7 +10945,12 @@
     @Override
     public void addPersistentPreferredActivity(ComponentName who, String callerPackageName,
             IntentFilter filter, ComponentName activity) {
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
 
         final int userId = caller.getUserId();
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -10949,7 +11000,12 @@
     @Override
     public void clearPackagePersistentPreferredActivities(ComponentName who,
             String callerPackageName, String packageName) {
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         final int userId = caller.getUserId();
 
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -11028,7 +11084,13 @@
     @Override
     public void setDefaultSmsApplication(ComponentName admin, String callerPackageName,
             String packageName, boolean parent) {
-        final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(admin, callerPackageName);
+        } else {
+            caller = getCallerIdentity(admin);
+        }
+
         final int userId;
 
         if (isPermissionCheckFlagEnabled()) {
@@ -11341,7 +11403,12 @@
     @Override
     public void addCrossProfileIntentFilter(ComponentName who, String callerPackageName,
             IntentFilter filter, int flags) {
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         int callingUserId = caller.getUserId();
 
         if (isPermissionCheckFlagEnabled()) {
@@ -11400,7 +11467,12 @@
 
     @Override
     public void clearCrossProfileIntentFilters(ComponentName who, String callerPackageName) {
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         int callingUserId = caller.getUserId();
 
         if (isPermissionCheckFlagEnabled()) {
@@ -12712,7 +12784,12 @@
             ComponentName who, String callerPackage, String key, boolean enabledFromThisOwner,
             boolean parent) {
 
-        final CallerIdentity caller = getCallerIdentity(who, callerPackage);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackage);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         int userId = caller.getUserId();
 
         if (!UserRestrictionsUtils.isValidRestriction(key)) {
@@ -12928,8 +13005,12 @@
         if (!mHasFeature) {
             return null;
         }
-
-        final CallerIdentity caller = getCallerIdentity(who, callerPackage);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackage);
+        } else {
+            caller = getCallerIdentity(who);
+        }
 
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
             EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
@@ -13406,7 +13487,12 @@
         if (!mHasFeature) {
             return;
         }
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         synchronized (getLockObject()) {
             final ActiveAdmin ap;
             if (isPermissionCheckFlagEnabled()) {
@@ -13456,12 +13542,19 @@
         if (!mHasFeature) {
             return null;
         }
+        CallerIdentity caller;
         Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
-        final CallerIdentity caller = getCallerIdentity(callerPackageName);
-        if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, caller.getPackageName(), userId)
-                && !hasFullCrossUsersPermission(caller, userId)) {
-            throw new SecurityException("Caller does not have permission to call this on user: "
-                    + userId);
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(callerPackageName);
+            if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
+                    caller.getPackageName(), userId)
+                    && !hasFullCrossUsersPermission(caller, userId)) {
+                throw new SecurityException("Caller does not have permission to call this on user: "
+                        + userId);
+            }
+        } else {
+            caller = getCallerIdentity();
+            Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
         }
 
         synchronized (getLockObject()) {
@@ -14047,11 +14140,13 @@
     public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
             throws SecurityException {
         Objects.requireNonNull(packages, "packages is null");
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
-        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES);
-        synchronized (getLockObject()) {
-            enforceCanCallLockTaskLocked(caller);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
         }
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES);
 
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
             EnforcingAdmin enforcingAdmin;
@@ -14103,7 +14198,12 @@
 
     @Override
     public String[] getLockTaskPackages(ComponentName who, String callerPackageName) {
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         final int userHandle = caller.getUserId();
 
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -14165,9 +14265,16 @@
         Preconditions.checkArgument(hasHome || !hasNotification,
             "Cannot use LOCK_TASK_FEATURE_NOTIFICATIONS without LOCK_TASK_FEATURE_HOME");
 
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         final int userHandle = caller.getUserId();
-        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES);
+        synchronized (getLockObject()) {
+            checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES);
+        }
 
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
             EnforcingAdmin enforcingAdmin;
@@ -14211,7 +14318,12 @@
 
     @Override
     public int getLockTaskFeatures(ComponentName who, String callerPackageName) {
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         final int userHandle = caller.getUserId();
 
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -14506,11 +14618,13 @@
 
     @Override
     public boolean setTime(@Nullable ComponentName who, String callerPackageName, long millis) {
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
         if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
             // This is a global action.
             enforcePermission(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL);
         } else {
+            caller = getCallerIdentity(who);
             Objects.requireNonNull(who, "ComponentName is null");
             Preconditions.checkCallAuthorization(
                     isDefaultDeviceOwner(caller)
@@ -14532,11 +14646,13 @@
     @Override
     public boolean setTimeZone(@Nullable ComponentName who, String callerPackageName,
             String timeZone) {
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
         if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
             // This is a global action.
             enforcePermission(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL);
         } else {
+            caller = getCallerIdentity(who);
             Objects.requireNonNull(who, "ComponentName is null");
             Preconditions.checkCallAuthorization(
                     isDefaultDeviceOwner(caller)
@@ -14745,7 +14861,12 @@
     @Override
     public boolean setStatusBarDisabled(ComponentName who, String callerPackageName,
             boolean disabled) {
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         if (isPermissionCheckFlagEnabled()) {
             enforcePermission(MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(),
                     UserHandle.USER_ALL);
@@ -16515,10 +16636,11 @@
         if (!mHasFeature) {
             return;
         }
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
         ActiveAdmin admin;
 
         if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
             EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                     who,
                     MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
@@ -16526,6 +16648,7 @@
                     caller.getUserId());
             admin = enforcingAdmin.getActiveAdmin();
         } else {
+            caller = getCallerIdentity(who);
             Objects.requireNonNull(who, "ComponentName is null");
             synchronized (getLockObject()) {
                 admin = getActiveAdminForUidLocked(who, caller.getUid());
@@ -16550,10 +16673,11 @@
         if (!mHasFeature) {
             return null;
         }
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
         ActiveAdmin admin;
 
         if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
             EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                     who,
                     MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
@@ -16561,6 +16685,7 @@
                     caller.getUserId());
             admin = enforcingAdmin.getActiveAdmin();
         } else {
+            caller = getCallerIdentity(who);
             Objects.requireNonNull(who, "ComponentName is null");
             synchronized (getLockObject()) {
                 admin = getActiveAdminForUidLocked(who, caller.getUid());
@@ -18157,7 +18282,12 @@
         if (token == null || token.length < 32) {
             throw new IllegalArgumentException("token must be at least 32-byte long");
         }
-        final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(admin, callerPackageName);
+        } else {
+            caller = getCallerIdentity(admin);
+        }
         final int userId = caller.getUserId();
 
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -18215,7 +18345,12 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return false;
         }
-        final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(admin, callerPackageName);
+        } else {
+            caller = getCallerIdentity(admin);
+        }
         final int userId = caller.getUserId();
         boolean result = false;
 
@@ -18257,7 +18392,12 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return false;
         }
-        final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(admin, callerPackageName);
+        } else {
+            caller = getCallerIdentity(admin);
+        }
         int userId = caller.getUserId();
 
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -18301,7 +18441,12 @@
         }
         Objects.requireNonNull(token);
 
-        final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(admin, callerPackageName);
+        } else {
+            caller = getCallerIdentity(admin);
+        }
 
         int userId = caller.getUserId();
         boolean result = false;
@@ -19483,7 +19628,12 @@
     public void setUserControlDisabledPackages(ComponentName who, String callerPackageName,
             List<String> packages) {
         Objects.requireNonNull(packages, "packages is null");
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         checkCanExecuteOrThrowUnsafe(
                 DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES);
 
@@ -19560,7 +19710,12 @@
     @Override
     public List<String> getUserControlDisabledPackages(ComponentName who,
             String callerPackageName) {
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
 
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
             enforceCanQuery(
@@ -19588,7 +19743,12 @@
     @Override
     public void setCommonCriteriaModeEnabled(ComponentName who, String callerPackageName,
             boolean enabled) {
-        final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(who, callerPackageName);
+        } else {
+            caller = getCallerIdentity(who);
+        }
         final ActiveAdmin admin;
 
         if (isPermissionCheckFlagEnabled()) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 6bc2d1f..e7b3dd9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -53,7 +53,6 @@
 import android.app.BackgroundStartPrivileges;
 import android.app.BroadcastOptions;
 import android.app.IApplicationThread;
-import android.app.ReceiverInfo;
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
@@ -66,7 +65,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
-import android.content.res.CompatibilityInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.DeadObjectException;
@@ -298,16 +296,12 @@
         };
 
         if (mImpl == Impl.DEFAULT) {
-            var q = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(), TAG,
+            mQueue = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(), TAG,
                     mConstants, mSkipPolicy, emptyHistory, false,
                     ProcessList.SCHED_GROUP_DEFAULT);
-            q.mReceiverBatch.mDeepReceiverCopy = true;
-            mQueue = q;
         } else if (mImpl == Impl.MODERN) {
-            var q = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
+            mQueue = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
                     mConstants, mConstants, mSkipPolicy, emptyHistory);
-            q.mReceiverBatch.mDeepReceiverCopy = true;
-            mQueue = q;
         } else {
             throw new UnsupportedOperationException();
         }
@@ -406,43 +400,6 @@
                 UnaryOperator.identity());
     }
 
-    private void doRegisteredReceiver(ProcessRecord r, boolean wedge, boolean abort,
-            UnaryOperator<Bundle> extrasOperator, ReceiverInfo info) {
-        final Intent intent = info.intent;
-        final Bundle extras = info.extras;
-        final boolean assumeDelivered = info.assumeDelivered;
-        mScheduledBroadcasts.add(makeScheduledBroadcast(r, intent));
-        if (!wedge && !assumeDelivered) {
-            assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
-            assertNotEquals(ProcessList.SCHED_GROUP_UNDEFINED,
-                    mQueue.getPreferredSchedulingGroupLocked(r));
-            mHandlerThread.getThreadHandler().post(() -> {
-                synchronized (mAms) {
-                    mQueue.finishReceiverLocked(r, Activity.RESULT_OK,
-                            null, extrasOperator.apply(extras), abort, false);
-                }
-            });
-        }
-    }
-
-    private void doManifestReceiver(ProcessRecord r, boolean wedge, boolean abort,
-            UnaryOperator<Bundle> extrasOperator, ReceiverInfo info) {
-        final Intent intent = info.intent;
-        final Bundle extras = info.extras;
-        mScheduledBroadcasts.add(makeScheduledBroadcast(r, intent));
-        if (!wedge) {
-            assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
-            assertNotEquals(ProcessList.SCHED_GROUP_UNDEFINED,
-                    mQueue.getPreferredSchedulingGroupLocked(r));
-            mHandlerThread.getThreadHandler().post(() -> {
-                synchronized (mAms) {
-                    mQueue.finishReceiverLocked(r, Activity.RESULT_OK, null,
-                            extrasOperator.apply(extras), abort, false);
-                }
-            });
-        }
-    }
-
     private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai, String processName,
             ProcessBehavior behavior, UnaryOperator<Bundle> extrasOperator) throws Exception {
         final boolean wedge = (behavior == ProcessBehavior.WEDGE);
@@ -486,22 +443,47 @@
         if (dead) return r;
 
         doAnswer((invocation) -> {
-            Log.v(TAG, "Intercepting scheduleReceiverList() for "
+            Log.v(TAG, "Intercepting scheduleReceiver() for "
                     + Arrays.toString(invocation.getArguments()));
-            final List<ReceiverInfo> data = invocation.getArgument(0);
-            for (int i = 0; i < data.size(); i++) {
-                ReceiverInfo info = data.get(i);
-                // The logic here mimics the logic in ActivityThread: elements of the list are
-                // forwarded to a handler for manifest receivers or to a handler for registered
-                // receivers.
-                if (info.registered) {
-                    doRegisteredReceiver(r, wedge, abort, extrasOperator, info);
-                } else {
-                    doManifestReceiver(r, wedge, abort, extrasOperator, info);
-                }
+            final Intent intent = invocation.getArgument(0);
+            final Bundle extras = invocation.getArgument(5);
+            mScheduledBroadcasts.add(makeScheduledBroadcast(r, intent));
+            if (!wedge) {
+                assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
+                assertNotEquals(ProcessList.SCHED_GROUP_UNDEFINED,
+                        mQueue.getPreferredSchedulingGroupLocked(r));
+                mHandlerThread.getThreadHandler().post(() -> {
+                    synchronized (mAms) {
+                        mQueue.finishReceiverLocked(r, Activity.RESULT_OK, null,
+                                extrasOperator.apply(extras), abort, false);
+                    }
+                });
             }
             return null;
-        }).when(thread).scheduleReceiverList(any());
+        }).when(thread).scheduleReceiver(any(), any(), any(), anyInt(), any(), any(), anyBoolean(),
+                anyBoolean(), anyInt(), anyInt(), anyInt(), any());
+
+        doAnswer((invocation) -> {
+            Log.v(TAG, "Intercepting scheduleRegisteredReceiver() for "
+                    + Arrays.toString(invocation.getArguments()));
+            final Intent intent = invocation.getArgument(1);
+            final Bundle extras = invocation.getArgument(4);
+            final boolean ordered = invocation.getArgument(5);
+            mScheduledBroadcasts.add(makeScheduledBroadcast(r, intent));
+            if (!wedge && ordered) {
+                assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
+                assertNotEquals(ProcessList.SCHED_GROUP_UNDEFINED,
+                        mQueue.getPreferredSchedulingGroupLocked(r));
+                mHandlerThread.getThreadHandler().post(() -> {
+                    synchronized (mAms) {
+                        mQueue.finishReceiverLocked(r, Activity.RESULT_OK,
+                                null, extrasOperator.apply(extras), abort, false);
+                    }
+                });
+            }
+            return null;
+        }).when(thread).scheduleRegisteredReceiver(any(), any(), anyInt(), any(), any(),
+                anyBoolean(), anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyInt(), any());
 
         return r;
     }
@@ -643,131 +625,6 @@
         };
     }
 
-    private static <T> boolean matchElement(T a, T b) {
-        return a == null || a.equals(b);
-    }
-    private static <T> boolean matchObject(ArgumentMatcher<T> m, T b) {
-        return m == null || m.matches(b);
-    }
-
-    /**
-     * Create an ArgumentMatcher for a manifest receiver.  The parameters are in the order of
-     * {@link IApplicationThread#scheduleReceiver} but the names correspond to the field names in
-     * {@link ReceiverInfo}.  For every parameter, a null means "don't care".
-     */
-    private ArgumentMatcher<ReceiverInfo> manifestReceiverMatcher(
-            ArgumentMatcher<Intent> intent,
-            ArgumentMatcher<ActivityInfo> activityInfo,
-            ArgumentMatcher<CompatibilityInfo> compatInfo,
-            Integer resultCode,
-            ArgumentMatcher<String> data,
-            ArgumentMatcher<Bundle> extras,
-            Boolean sync,
-            Boolean assumeDelivered,
-            Integer sendingUser,
-            Integer processState) {
-        return (test) -> {
-            return test.registered == false
-                    && matchObject(intent, test.intent)
-                    && matchObject(activityInfo, test.activityInfo)
-                    && matchObject(compatInfo, test.compatInfo)
-                    && matchElement(resultCode, test.resultCode)
-                    && matchObject(data, test.data)
-                    && matchObject(extras, test.extras)
-                    && matchElement(sync, test.sync)
-                    && matchElement(assumeDelivered, test.assumeDelivered)
-                    && matchElement(sendingUser, test.sendingUser)
-                    && matchElement(processState, test.processState);
-        };
-    }
-
-
-    /**
-     * Create an argument suitable for the verify() mock methods, when the goal is to find a call
-     * containing a manifest receiver.
-     */
-    private List<ReceiverInfo> manifestReceiver(
-            ArgumentMatcher<Intent> intent,
-            ArgumentMatcher<ActivityInfo> activityInfo,
-            ArgumentMatcher<CompatibilityInfo> compatInfo,
-            Integer resultCode,
-            ArgumentMatcher<String> data,
-            ArgumentMatcher<Bundle> extras,
-            Boolean sync,
-            Boolean assumeDelivered,
-            Integer sendingUser,
-            Integer processState) {
-        return argThat(receiverList(manifestReceiverMatcher(intent, activityInfo, compatInfo,
-                resultCode, data, extras, sync, assumeDelivered,
-                sendingUser, processState)));
-    }
-
-    /**
-     * Create an ArgumentMatcher for a registered receiver.  The parameters are in the order of
-     * {@link IApplicationThread#scheduleRegisteredReceiver} but the names correspond to the field
-     * names in {@link ReceiverInfo}.  For every parameter, a null means "don't care".
-     */
-    private ArgumentMatcher<ReceiverInfo> registeredReceiverMatcher(
-            ArgumentMatcher<IIntentReceiver> receiver,
-            ArgumentMatcher<Intent> intent,
-            Integer resultCode,
-            ArgumentMatcher<String> data,
-            ArgumentMatcher<Bundle> extras,
-            Boolean ordered,
-            Boolean sticky,
-            Boolean assumeDelivered,
-            Integer sendingUser,
-            Integer processState) {
-        return (test) -> {
-            return test.registered == true
-                    && matchObject(receiver, test.receiver)
-                    && matchObject(intent, test.intent)
-                    && matchElement(resultCode, test.resultCode)
-                    && matchObject(data, test.data)
-                    && matchObject(extras, test.extras)
-                    && matchElement(ordered, test.ordered)
-                    && matchElement(sticky, test.sticky)
-                    && matchElement(assumeDelivered, test.assumeDelivered)
-                    && matchElement(sendingUser, test.sendingUser)
-                    && matchElement(processState, test.processState);
-        };
-    }
-
-    /**
-     * Create an argument suitable for the verify() mock methods, when the goal is to find a call
-     * containing a registered receiver.
-     */
-    private List<ReceiverInfo> registeredReceiver(
-            ArgumentMatcher<IIntentReceiver> receiver,
-            ArgumentMatcher<Intent> intent,
-            Integer resultCode,
-            ArgumentMatcher<String> data,
-            ArgumentMatcher<Bundle> extras,
-            Boolean ordered,
-            Boolean sticky,
-            Boolean assumeDelivered,
-            Integer sendingUser,
-            Integer processState) {
-        return argThat(receiverList(registeredReceiverMatcher(receiver, intent, resultCode,
-                data, extras, ordered, sticky, assumeDelivered,
-                sendingUser, processState)));
-    }
-
-    /**
-     * Apply a matcher to every element in a ReceiverInfo list.
-     */
-    private ArgumentMatcher<List<ReceiverInfo>> receiverList(ArgumentMatcher<ReceiverInfo> a) {
-        return (test) -> {
-            for (int i = 0; i < test.size(); i++) {
-                ReceiverInfo r = test.get(i);
-                if (a.matches(r)) {
-                    return true;
-                }
-            }
-            return false;
-        };
-    }
-
     private ArgumentMatcher<Bundle> bundleEquals(Bundle bundle) {
         return (test) -> {
             // TODO: check values in addition to keys
@@ -802,37 +659,42 @@
             ComponentName component) throws Exception {
         final Intent targetedIntent = new Intent(intent);
         targetedIntent.setComponent(component);
-        verify(app.getThread(), mode).scheduleReceiverList(
-                manifestReceiver(filterEquals(targetedIntent),
-                        null, null, null, null, null, null, null, UserHandle.USER_SYSTEM, null));
+        verify(app.getThread(), mode).scheduleReceiver(
+                argThat(filterEquals(targetedIntent)), any(), any(),
+                anyInt(), any(), any(), eq(false), anyBoolean(), eq(UserHandle.USER_SYSTEM),
+                anyInt(), anyInt(), any());
     }
 
     private void verifyScheduleReceiver(VerificationMode mode, ProcessRecord app,
             Intent intent, int userId) throws Exception {
-        verify(app.getThread(), mode).scheduleReceiverList(
-                manifestReceiver(filterEqualsIgnoringComponent(intent),
-                        null, null, null, null, null, null, null, userId, null));
+        verify(app.getThread(), mode).scheduleReceiver(
+                argThat(filterEqualsIgnoringComponent(intent)), any(), any(),
+                anyInt(), any(), any(), anyBoolean(), anyBoolean(), eq(userId),
+                anyInt(), anyInt(), any());
     }
 
     private void verifyScheduleReceiver(VerificationMode mode, ProcessRecord app,
             int userId) throws Exception {
-        verify(app.getThread(), mode).scheduleReceiverList(
-                manifestReceiver(null,
-                        null, null, null, null, null, null, null, userId, null));
+        verify(app.getThread(), mode).scheduleReceiver(
+                any(), any(), any(),
+                anyInt(), any(), any(), anyBoolean(), anyBoolean(), eq(userId),
+                anyInt(), anyInt(), any());
     }
 
     private void verifyScheduleRegisteredReceiver(ProcessRecord app,
             Intent intent) throws Exception {
-        verify(app.getThread()).scheduleReceiverList(
-                registeredReceiver(null, filterEqualsIgnoringComponent(intent),
-                        null, null, null, null, null, null, UserHandle.USER_SYSTEM, null));
+        verify(app.getThread()).scheduleRegisteredReceiver(
+                any(), argThat(filterEqualsIgnoringComponent(intent)),
+                anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+                eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
     }
 
     private void verifyScheduleRegisteredReceiver(VerificationMode mode, ProcessRecord app,
             int userId) throws Exception {
-        verify(app.getThread(), mode).scheduleReceiverList(
-                registeredReceiver(null, null,
-                        null, null, null, null, null, null, userId, null));
+        verify(app.getThread(), mode).scheduleRegisteredReceiver(
+                any(), any(),
+                anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+                eq(userId), anyInt(), anyInt(), any());
     }
 
     static final int USER_GUEST = 11;
@@ -1485,24 +1347,25 @@
         final InOrder inOrder = inOrder(greenThread, blueThread, yellowThread, redThread);
         final Bundle expectedExtras = new Bundle();
         expectedExtras.putBoolean(PACKAGE_RED, true);
-        inOrder.verify(greenThread).scheduleReceiverList(manifestReceiver(
-                filterEqualsIgnoringComponent(airplane), null, null,
-                Activity.RESULT_OK, null, bundleEquals(expectedExtras), true, false,
-                UserHandle.USER_SYSTEM, null));
-        inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
-                filterEqualsIgnoringComponent(airplane), null, null,
-                Activity.RESULT_OK, null, bundleEquals(expectedExtras), true, false,
-                UserHandle.USER_SYSTEM, null));
+        inOrder.verify(greenThread).scheduleReceiver(
+                argThat(filterEqualsIgnoringComponent(airplane)), any(), any(),
+                eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)),
+                eq(true), eq(false), eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
+        inOrder.verify(blueThread).scheduleReceiver(
+                argThat(filterEqualsIgnoringComponent(airplane)), any(), any(),
+                eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)),
+                eq(true), eq(false), eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
         expectedExtras.putBoolean(PACKAGE_BLUE, true);
-        inOrder.verify(yellowThread).scheduleReceiverList(manifestReceiver(
-                filterEqualsIgnoringComponent(airplane), null, null,
-                Activity.RESULT_OK, null, bundleEquals(expectedExtras), true, false,
-                UserHandle.USER_SYSTEM, null));
+        inOrder.verify(yellowThread).scheduleReceiver(
+                argThat(filterEqualsIgnoringComponent(airplane)), any(), any(),
+                eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)),
+                eq(true), eq(false), eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
         expectedExtras.putBoolean(PACKAGE_YELLOW, true);
-        inOrder.verify(redThread).scheduleReceiverList(registeredReceiver(
-                null, filterEquals(airplane),
-                Activity.RESULT_OK, null, bundleEquals(expectedExtras), false,
-                null, true, UserHandle.USER_SYSTEM, null));
+        inOrder.verify(redThread).scheduleRegisteredReceiver(
+                any(), argThat(filterEquals(airplane)),
+                eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)),
+                eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM), anyInt(),
+                anyInt(), any());
 
         // Finally, verify that we thawed the final receiver
         verify(mAms.mOomAdjuster.mCachedAppOptimizer).unfreezeTemporarily(eq(callerApp),
@@ -1565,24 +1428,25 @@
         // have invoked or skipped the second receiver depending on the intent
         // flag policy; we always deliver to final receiver regardless of abort
         final InOrder inOrder = inOrder(greenThread, blueThread, redThread);
-        inOrder.verify(greenThread).scheduleReceiverList(manifestReceiver(
-                filterEqualsIgnoringComponent(intent), null, null,
-                Activity.RESULT_OK, null, null, true, false, UserHandle.USER_SYSTEM,
-                null));
+        inOrder.verify(greenThread).scheduleReceiver(
+                argThat(filterEqualsIgnoringComponent(intent)), any(), any(),
+                eq(Activity.RESULT_OK), any(), any(), eq(true), eq(false),
+                eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
         if ((intent.getFlags() & Intent.FLAG_RECEIVER_NO_ABORT) != 0) {
-            inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
-                    filterEqualsIgnoringComponent(intent), null, null,
-                    Activity.RESULT_OK, null, null, true, false, UserHandle.USER_SYSTEM,
-                    null));
+            inOrder.verify(blueThread).scheduleReceiver(
+                    argThat(filterEqualsIgnoringComponent(intent)), any(), any(),
+                    eq(Activity.RESULT_OK), any(), any(), eq(true), eq(false),
+                    eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
         } else {
-            inOrder.verify(blueThread, never()).scheduleReceiverList(manifestReceiver(
-                    null, null, null, null, null,
-                    null, null, null, null, null));
+            inOrder.verify(blueThread, never()).scheduleReceiver(
+                    any(), any(), any(), anyInt(), any(), any(),
+                    anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyInt(), any());
         }
-        inOrder.verify(redThread).scheduleReceiverList(registeredReceiver(
-                null, filterEquals(intent),
-                Activity.RESULT_OK, null, bundleEquals(expectedExtras),
-                false, null, true, UserHandle.USER_SYSTEM, null));
+        inOrder.verify(redThread).scheduleRegisteredReceiver(
+                any(), argThat(filterEquals(intent)),
+                eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)),
+                eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM),
+                anyInt(), anyInt(), any());
     }
 
     /**
@@ -1602,10 +1466,11 @@
                 orderedResultTo, orderedExtras));
 
         waitForIdle();
-        verify(callerThread).scheduleReceiverList(registeredReceiver(
-                null, filterEquals(airplane),
-                Activity.RESULT_OK, null, bundleEquals(orderedExtras), false,
-                null, true, UserHandle.USER_SYSTEM, null));
+        verify(callerThread).scheduleRegisteredReceiver(
+                any(), argThat(filterEquals(airplane)),
+                eq(Activity.RESULT_OK), any(), argThat(bundleEquals(orderedExtras)),
+                eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM),
+                anyInt(), anyInt(), any());
     }
 
     /**
@@ -1623,10 +1488,11 @@
                         makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE)), resultTo));
 
         waitForIdle();
-        verify(callerThread).scheduleReceiverList(registeredReceiver(
-                null, filterEquals(airplane),
-                Activity.RESULT_OK, null, null, false,
-                null, true, UserHandle.USER_SYSTEM, null));
+        verify(callerThread).scheduleRegisteredReceiver(
+                any(), argThat(filterEquals(airplane)),
+                eq(Activity.RESULT_OK), any(), any(),
+                eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM),
+                anyInt(), anyInt(), any());
     }
 
     /**
@@ -1790,28 +1656,34 @@
         final InOrder inOrder = inOrder(callerThread, blueThread);
 
         // First broadcast is canceled
-        inOrder.verify(callerThread).scheduleReceiverList(registeredReceiver(null,
-                filterAndExtrasEquals(timezoneFirst), Activity.RESULT_CANCELED, null,
-                null, false, null, true, UserHandle.USER_SYSTEM, null));
+        inOrder.verify(callerThread).scheduleRegisteredReceiver(
+                any(), argThat(filterAndExtrasEquals(timezoneFirst)),
+                eq(Activity.RESULT_CANCELED), any(), any(),
+                eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM),
+                anyInt(), anyInt(), any());
 
         // We deliver second broadcast to app
         timezoneSecond.setClassName(PACKAGE_BLUE, CLASS_GREEN);
-        inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
-                filterAndExtrasEquals(timezoneSecond),
-                null, null, null, null, null, true, false, null, null));
+        inOrder.verify(blueThread).scheduleReceiver(
+                argThat(filterAndExtrasEquals(timezoneSecond)), any(), any(),
+                anyInt(), any(), any(), eq(true), eq(false), anyInt(),
+                anyInt(), anyInt(), any());
 
         // Second broadcast is finished
         timezoneSecond.setComponent(null);
-        inOrder.verify(callerThread).scheduleReceiverList(registeredReceiver(null,
-                filterAndExtrasEquals(timezoneSecond), Activity.RESULT_OK, null,
-                null, false, null, true, UserHandle.USER_SYSTEM, null));
+        inOrder.verify(callerThread).scheduleRegisteredReceiver(
+                any(), argThat(filterAndExtrasEquals(timezoneSecond)),
+                eq(Activity.RESULT_OK), any(), any(),
+                eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM),
+                anyInt(), anyInt(), any());
 
         // Since we "replaced" the first broadcast in its original position,
         // only now do we see the airplane broadcast
         airplane.setClassName(PACKAGE_BLUE, CLASS_RED);
-        inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
-                filterEquals(airplane),
-                null, null, null, null, null, false, false, null, null));
+        inOrder.verify(blueThread).scheduleReceiver(
+                argThat(filterEquals(airplane)), any(), any(),
+                anyInt(), any(), any(), eq(false), eq(false), anyInt(),
+                anyInt(), anyInt(), any());
     }
 
     @Test
@@ -1873,15 +1745,17 @@
 
         // First broadcast is delivered.
         timeTickFirst.setClassName(PACKAGE_BLUE, CLASS_BLUE);
-        inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
-                filterAndExtrasEquals(timeTickFirst),
-                null, null, null, null, null, false, false, null, null));
+        inOrder.verify(blueThread).scheduleReceiver(
+                argThat(filterAndExtrasEquals(timeTickFirst)), any(), any(),
+                anyInt(), any(), any(), eq(false), eq(false), anyInt(),
+                anyInt(), anyInt(), any());
 
         // Second broadcast should be replaced by third broadcast.
         timeTickThird.setClassName(PACKAGE_BLUE, CLASS_BLUE);
-        inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
-                filterAndExtrasEquals(timeTickThird),
-                null, null, null, null, null, false, false, null, null));
+        inOrder.verify(blueThread).scheduleReceiver(
+                argThat(filterAndExtrasEquals(timeTickThird)), any(), any(),
+                anyInt(), any(), any(), eq(false), eq(false), anyInt(),
+                anyInt(), anyInt(), any());
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 6e63315..3d03b47 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -460,6 +460,25 @@
         verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
     }
 
+    @Test
+    public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
+        // We should still set screen state for the default display
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1);
+        verify(mHolder.displayPowerState).setScreenState(anyInt());
+
+        mHolder = createDisplayPowerController(42, UNIQUE_ID);
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1);
+        verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+
+        mHolder.dpc.onBootCompleted();
+        advanceTime(1);
+        verify(mHolder.displayPowerState).setScreenState(anyInt());
+    }
+
     private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
             String uniqueId) {
         final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
@@ -487,7 +506,7 @@
                 mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, display,
                 mBrightnessTrackerMock, brightnessSetting, () -> {},
-                hbmMetadata);
+                hbmMetadata, false);
 
         return new DisplayPowerControllerHolder(dpc, displayPowerState, brightnessSetting, animator,
                 automaticBrightnessController, wakelockController);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index a8c3e4e..841128e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -464,6 +464,25 @@
         verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
     }
 
+    @Test
+    public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
+        // We should still set screen state for the default display
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1);
+        verify(mHolder.displayPowerState).setScreenState(anyInt());
+
+        mHolder = createDisplayPowerController(42, UNIQUE_ID);
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1);
+        verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+
+        mHolder.dpc.onBootCompleted();
+        advanceTime(1);
+        verify(mHolder.displayPowerState).setScreenState(anyInt());
+    }
+
     private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
             String uniqueId) {
         final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
@@ -489,7 +508,7 @@
                 mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, display,
                 mBrightnessTrackerMock, brightnessSetting, () -> {},
-                hbmMetadata);
+                hbmMetadata, false);
 
         return new DisplayPowerControllerHolder(dpc, displayPowerState, brightnessSetting, animator,
                 automaticBrightnessController);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 95009e6..0b6756d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -1122,7 +1122,7 @@
 
     @Nullable
     private UserInfo getUser(int id) {
-        List<UserInfo> list = mUserManager.getAliveUsers();
+        List<UserInfo> list = mUserManager.getUsers();
 
         for (UserInfo user : list) {
             if (user.id == id) {
@@ -1277,7 +1277,7 @@
     @MediumTest
     @Test
     public void testConcurrentUserCreate() throws Exception {
-        int userCount = mUserManager.getAliveUsers().size();
+        int userCount = mUserManager.getUsers().size();
         int maxSupportedUsers = UserManager.getMaxSupportedUsers();
         int canBeCreatedCount = maxSupportedUsers - userCount;
         // Test exceeding the limit while running in parallel
@@ -1300,7 +1300,7 @@
                 "Could not create " + createUsersCount + " users in " + timeout + " seconds")
                 .that(es.awaitTermination(timeout, TimeUnit.SECONDS))
                 .isTrue();
-        assertThat(mUserManager.getAliveUsers().size()).isEqualTo(maxSupportedUsers);
+        assertThat(mUserManager.getUsers().size()).isEqualTo(maxSupportedUsers);
         assertThat(created.get()).isEqualTo(canBeCreatedCount);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRemovalWaiter.java b/services/tests/servicestests/src/com/android/server/pm/UserRemovalWaiter.java
index 9e1af0c..40eb84e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRemovalWaiter.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRemovalWaiter.java
@@ -23,7 +23,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.util.Log;
 
 import java.io.Closeable;
@@ -36,9 +35,8 @@
 public class UserRemovalWaiter extends BroadcastReceiver implements Closeable {
 
     private final Context mContext;
-    private final UserManager mUserManager;
     private final String mTag;
-    private final long mTimeoutMillis;
+    private final long mTimeoutInSeconds;
     private final Map<Integer, CountDownLatch> mMap = new ConcurrentHashMap<>();
 
     private CountDownLatch getLatch(final int userId) {
@@ -47,9 +45,8 @@
 
     public UserRemovalWaiter(Context context, String tag, int timeoutInSeconds) {
         mContext = context;
-        mUserManager = UserManager.get(mContext);
         mTag = tag;
-        mTimeoutMillis = timeoutInSeconds * 1000L;
+        mTimeoutInSeconds = timeoutInSeconds;
 
         mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_USER_REMOVED));
     }
@@ -73,28 +70,17 @@
      */
     public void waitFor(int userId) {
         Log.i(mTag, "Waiting for user " + userId + " to be removed");
-        CountDownLatch latch = getLatch(userId);
+
         long startTime = System.currentTimeMillis();
-        while (System.currentTimeMillis() - startTime < mTimeoutMillis) {
-            if (hasUserGone(userId) || waitLatchForOneSecond(latch)) {
+        try {
+            if (getLatch(userId).await(mTimeoutInSeconds, TimeUnit.SECONDS)) {
                 Log.i(mTag, "User " + userId + " is removed in "
                         + (System.currentTimeMillis() - startTime) + " ms");
-                return;
+            } else {
+                fail("Timeout waiting for user removal. userId = " + userId);
             }
-        }
-        fail("Timeout waiting for user removal. userId = " + userId);
-    }
-
-    private boolean hasUserGone(int userId) {
-        return mUserManager.getAliveUsers().stream().noneMatch(x -> x.id == userId);
-    }
-
-    private boolean waitLatchForOneSecond(CountDownLatch latch) {
-        try {
-            return latch.await(1, TimeUnit.SECONDS);
         } catch (InterruptedException e) {
-            Log.e(mTag, "Thread interrupted unexpectedly.", e);
-            return false;
+            throw new AssertionError("Thread interrupted unexpectedly.", e);
         }
     }
 }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index b716690..5efd158 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -56,9 +56,12 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.UUID;
+import java.util.function.Function;
+import java.util.function.Supplier;
 
 /**
  * Helper for {@link SoundTrigger} APIs. Supports two types of models:
@@ -74,6 +77,9 @@
     static final String TAG = "SoundTriggerHelper";
     static final boolean DBG = false;
 
+    // Module ID if there is no available module to connect to.
+    public static final int INVALID_MODULE_ID = -1;
+
     /**
      * Return codes for {@link #startRecognition(int, KeyphraseSoundModel,
      *      IRecognitionStatusCallback, RecognitionConfig)},
@@ -84,10 +90,6 @@
 
     private static final int INVALID_VALUE = Integer.MIN_VALUE;
 
-    /** The {@link ModuleProperties} for the system, or null if none exists. */
-    final ModuleProperties mModuleProperties;
-
-    /** The properties for the DSP module */
     private SoundTriggerModule mModule;
     private final Object mLock = new Object();
     private final Context mContext;
@@ -114,7 +116,6 @@
 
     private PowerSaveModeListener mPowerSaveModeListener;
 
-    private final SoundTriggerModuleProvider mModuleProvider;
 
     // Handler to process call state changes will delay to allow time for the audio
     // and sound trigger HALs to process the end of call notifications
@@ -123,46 +124,28 @@
     private static final int MSG_CALL_STATE_CHANGED = 0;
     private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000;
 
-    /**
-     * Provider interface for retrieving SoundTriggerModule instances
-     */
-    public interface SoundTriggerModuleProvider {
-        /**
-         * Populate module properties for all available modules
-         *
-         * @param modules List of ModuleProperties to be populated
-         * @return Status int 0 on success.
-         */
-        int listModuleProperties(@NonNull ArrayList<SoundTrigger.ModuleProperties> modules);
+    // TODO(b/269366605) Temporary solution to query correct moduleProperties
+    private final int mModuleId;
+    private final Function<SoundTrigger.StatusListener, SoundTriggerModule> mModuleProvider;
+    private final Supplier<List<ModuleProperties>> mModulePropertiesProvider;
 
-        /**
-         * Get SoundTriggerModule based on {@link SoundTrigger.ModuleProperties#getId()}
-         *
-         * @param moduleId Module ID
-         * @param statusListener Client listener to be associated with the returned module
-         * @return Module associated with moduleId
-         */
-        SoundTriggerModule getModule(int moduleId, SoundTrigger.StatusListener statusListener);
-    }
-
-    SoundTriggerHelper(Context context, SoundTriggerModuleProvider moduleProvider) {
-        ArrayList <ModuleProperties> modules = new ArrayList<>();
-        mModuleProvider = moduleProvider;
-        int status = mModuleProvider.listModuleProperties(modules);
+    SoundTriggerHelper(Context context,
+            @NonNull Function<SoundTrigger.StatusListener, SoundTriggerModule> moduleProvider,
+            int moduleId,
+            @NonNull Supplier<List<ModuleProperties>> modulePropertiesProvider) {
+        mModuleId = moduleId;
         mContext = context;
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mModelDataMap = new HashMap<UUID, ModelData>();
         mKeyphraseUuidMap = new HashMap<Integer, UUID>();
-        if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
-            Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size());
-            mModuleProperties = null;
+        mModuleProvider = moduleProvider;
+        mModulePropertiesProvider = modulePropertiesProvider;
+        if (moduleId == INVALID_MODULE_ID) {
             mModule = null;
         } else {
-            // TODO: Figure out how to determine which module corresponds to the DSP hardware.
-            mModuleProperties = modules.get(0);
+            mModule = mModuleProvider.apply(this);
         }
-
         Looper looper = Looper.myLooper();
         if (looper == null) {
             looper = Looper.getMainLooper();
@@ -245,7 +228,6 @@
                         + " soundModel=" + soundModel + ", callback=" + callback.asBinder()
                         + ", recognitionConfig=" + recognitionConfig
                         + ", runInBatterySaverMode=" + runInBatterySaverMode);
-                Slog.d(TAG, "moduleProperties=" + mModuleProperties);
                 dumpModelStateLocked();
             }
 
@@ -290,11 +272,8 @@
 
     private int prepareForRecognition(ModelData modelData) {
         if (mModule == null) {
-            mModule = mModuleProvider.getModule(mModuleProperties.getId(), this);
-            if (mModule == null) {
-                Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module");
-                return STATUS_ERROR;
-            }
+            Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module");
+            return STATUS_ERROR;
         }
         // Load the model if it is not loaded.
         if (!modelData.isModelLoaded()) {
@@ -336,11 +315,6 @@
             IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig,
             int keyphraseId, boolean runInBatterySaverMode) {
         synchronized (mLock) {
-            if (mModuleProperties == null) {
-                Slog.w(TAG, "Attempting startRecognition without the capability");
-                return STATUS_ERROR;
-            }
-
             IRecognitionStatusCallback oldCallback = modelData.getCallback();
             if (oldCallback != null && oldCallback.asBinder() != callback.asBinder()) {
                 Slog.w(TAG, "Canceling previous recognition for model id: "
@@ -486,8 +460,8 @@
             if (callback == null) {
                 return STATUS_ERROR;
             }
-            if (mModuleProperties == null || mModule == null) {
-                Slog.w(TAG, "Attempting stopRecognition without the capability");
+            if (mModule == null) {
+                Slog.w(TAG, "Attempting stopRecognition after detach");
                 return STATUS_ERROR;
             }
 
@@ -563,7 +537,13 @@
     }
 
     public ModuleProperties getModuleProperties() {
-        return mModuleProperties;
+        for (ModuleProperties moduleProperties : mModulePropertiesProvider.get()) {
+            if (moduleProperties.getId() == mModuleId) {
+                return moduleProperties;
+            }
+        }
+        Slog.e(TAG, "Module properties not found for existing moduleId " + mModuleId);
+        return null;
     }
 
     int unloadKeyphraseSoundModel(int keyphraseId) {
@@ -1027,7 +1007,7 @@
             internalClearGlobalStateLocked();
             if (mModule != null) {
                 mModule.detach();
-                mModule = null;
+                mModule = mModuleProvider.apply(this);
             }
         }
     }
@@ -1098,7 +1078,6 @@
     void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mLock) {
             pw.print("  module properties=");
-            pw.println(mModuleProperties == null ? "null" : mModuleProperties);
             pw.print("  call active=");
             pw.println(mCallActive);
             pw.println("  SoundTrigger Power State=" + mSoundTriggerPowerSaveMode);
@@ -1444,7 +1423,7 @@
     // Computes whether we have any recognition running at all (voice or generic). Sets
     // the mRecognitionRequested variable with the result.
     private boolean computeRecognitionRequestedLocked() {
-        if (mModuleProperties == null || mModule == null) {
+        if (mModule == null) {
             mRecognitionRequested = false;
             return mRecognitionRequested;
         }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
index 7071e23a..cc398d9 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
@@ -26,12 +26,14 @@
 import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
 import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.media.permission.Identity;
 import android.os.IBinder;
 
 import com.android.server.voiceinteraction.VoiceInteractionManagerService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.List;
 
 /**
  * Provides a local service for managing voice-related recoginition models. This is primarily used
@@ -46,7 +48,11 @@
     int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
     int STATUS_OK = SoundTrigger.STATUS_OK;
 
-    Session attach(@NonNull IBinder client);
+    // Attach to a specific underlying STModule
+    Session attach(@NonNull IBinder client, ModuleProperties underlyingModule);
+
+    // Enumerate possible STModules to attach to
+    List<ModuleProperties> listModuleProperties(Identity originatorIdentity);
 
     /**
      * Dumps service-wide information.
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 7824c69..495a433 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -51,7 +51,6 @@
 import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
 import android.hardware.soundtrigger.SoundTrigger.SoundModel;
-import android.hardware.soundtrigger.SoundTriggerModule;
 import android.media.AudioAttributes;
 import android.media.AudioFormat;
 import android.media.AudioRecord;
@@ -89,6 +88,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.TreeMap;
@@ -215,47 +215,73 @@
         }
     }
 
-    private SoundTriggerHelper newSoundTriggerHelper() {
+    private SoundTriggerHelper newSoundTriggerHelper(ModuleProperties moduleProperties) {
         Identity middlemanIdentity = new Identity();
         middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
-
         Identity originatorIdentity = IdentityContext.getNonNull();
 
-        return new SoundTriggerHelper(mContext,
-                new SoundTriggerHelper.SoundTriggerModuleProvider() {
-                    @Override
-                    public int listModuleProperties(ArrayList<ModuleProperties> modules) {
-                        return SoundTrigger.listModulesAsMiddleman(modules, middlemanIdentity,
-                                originatorIdentity);
-                    }
+        ArrayList<ModuleProperties> moduleList = new ArrayList<>();
+        SoundTrigger.listModulesAsMiddleman(moduleList, middlemanIdentity,
+                                        originatorIdentity);
 
-                    @Override
-                    public SoundTriggerModule getModule(int moduleId,
-                            SoundTrigger.StatusListener statusListener) {
-                        return SoundTrigger.attachModuleAsMiddleman(moduleId, statusListener, null,
-                                middlemanIdentity, originatorIdentity);
-                    }
+        // Don't fail existing CTS tests which run without a ST module
+        final int moduleId = (moduleProperties != null) ?
+                moduleProperties.getId() : SoundTriggerHelper.INVALID_MODULE_ID;
+
+        if (moduleId != SoundTriggerHelper.INVALID_MODULE_ID) {
+            if (!moduleList.contains(moduleProperties)) {
+                throw new IllegalArgumentException("Invalid module properties");
+            }
+        }
+
+        return new SoundTriggerHelper(
+                mContext,
+                (SoundTrigger.StatusListener statusListener) ->
+                                        SoundTrigger.attachModuleAsMiddleman(
+                                        moduleId, statusListener, null /* handler */,
+                                        middlemanIdentity, originatorIdentity),
+                moduleId,
+                () -> {
+                    ArrayList<ModuleProperties> modulePropList = new ArrayList<>();
+                    SoundTrigger.listModulesAsMiddleman(modulePropList, middlemanIdentity,
+                                                    originatorIdentity);
+                    return modulePropList;
                 });
     }
 
     class SoundTriggerServiceStub extends ISoundTriggerService.Stub {
         @Override
-        public ISoundTriggerSession attachAsOriginator(Identity originatorIdentity,
+        public ISoundTriggerSession attachAsOriginator(@NonNull Identity originatorIdentity,
+                @NonNull ModuleProperties moduleProperties,
                 @NonNull IBinder client) {
             try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
                     originatorIdentity)) {
-                return new SoundTriggerSessionStub(client);
+                return new SoundTriggerSessionStub(client, newSoundTriggerHelper(moduleProperties));
             }
         }
 
         @Override
-        public ISoundTriggerSession attachAsMiddleman(Identity originatorIdentity,
-                Identity middlemanIdentity,
+        public ISoundTriggerSession attachAsMiddleman(@NonNull Identity originatorIdentity,
+                @NonNull Identity middlemanIdentity,
+                @NonNull ModuleProperties moduleProperties,
                 @NonNull IBinder client) {
             try (SafeCloseable ignored = PermissionUtil.establishIdentityIndirect(mContext,
                     SOUNDTRIGGER_DELEGATE_IDENTITY, middlemanIdentity,
                     originatorIdentity)) {
-                return new SoundTriggerSessionStub(client);
+                return new SoundTriggerSessionStub(client, newSoundTriggerHelper(moduleProperties));
+            }
+        }
+
+        @Override
+        public List<ModuleProperties> listModuleProperties(@NonNull Identity originatorIdentity) {
+            try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
+                    originatorIdentity)) {
+                Identity middlemanIdentity = new Identity();
+                middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
+                ArrayList<ModuleProperties> moduleList = new ArrayList<>();
+                SoundTrigger.listModulesAsMiddleman(moduleList, middlemanIdentity,
+                                                originatorIdentity);
+                return moduleList;
             }
         }
     }
@@ -269,8 +295,8 @@
         private final Object mCallbacksLock = new Object();
         private final TreeMap<UUID, IRecognitionStatusCallback> mCallbacks = new TreeMap<>();
 
-        SoundTriggerSessionStub(@NonNull IBinder client) {
-            mSoundTriggerHelper = newSoundTriggerHelper();
+        SoundTriggerSessionStub(@NonNull IBinder client, SoundTriggerHelper soundTriggerHelper) {
+            mSoundTriggerHelper = soundTriggerHelper;
             mClient = client;
             mOriginatorIdentity = IdentityContext.getNonNull();
             try {
@@ -1615,8 +1641,18 @@
         }
 
         @Override
-        public Session attach(@NonNull IBinder client) {
-            return new SessionImpl(newSoundTriggerHelper(), client);
+        public Session attach(@NonNull IBinder client, ModuleProperties underlyingModule) {
+            return new SessionImpl(newSoundTriggerHelper(underlyingModule), client);
+        }
+
+        @Override
+        public List<ModuleProperties> listModuleProperties(Identity originatorIdentity) {
+            Identity middlemanIdentity = new Identity();
+            middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
+            ArrayList<ModuleProperties> moduleList = new ArrayList<>();
+            SoundTrigger.listModulesAsMiddleman(moduleList, middlemanIdentity,
+                                            originatorIdentity);
+            return moduleList;
         }
 
         @Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 717f4e7..fd54293 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -377,7 +377,8 @@
 
         @Override
         public @NonNull IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator(
-                @NonNull Identity originatorIdentity, IBinder client) {
+                @NonNull Identity originatorIdentity, IBinder client,
+                @NonNull ModuleProperties moduleProperties) {
             Objects.requireNonNull(originatorIdentity);
             boolean forHotwordDetectionService;
             synchronized (VoiceInteractionManagerServiceStub.this) {
@@ -396,7 +397,7 @@
                 originatorIdentity.uid = Binder.getCallingUid();
                 originatorIdentity.pid = Binder.getCallingPid();
                 session = new SoundTriggerSessionPermissionsDecorator(
-                        createSoundTriggerSessionForSelfIdentity(client),
+                        createSoundTriggerSessionForSelfIdentity(client, moduleProperties),
                         mContext,
                         originatorIdentity);
             } else {
@@ -405,25 +406,35 @@
                 }
                 try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
                         originatorIdentity)) {
-                    session = new SoundTriggerSession(mSoundTriggerInternal.attach(client));
+                    session = new SoundTriggerSession(mSoundTriggerInternal.attach(client,
+                                moduleProperties));
                 }
             }
             return new SoundTriggerSessionBinderProxy(session);
         }
 
         private IVoiceInteractionSoundTriggerSession createSoundTriggerSessionForSelfIdentity(
-                IBinder client) {
+                IBinder client, ModuleProperties moduleProperties) {
             Identity identity = new Identity();
             identity.uid = Process.myUid();
             identity.pid = Process.myPid();
             identity.packageName = ActivityThread.currentOpPackageName();
             return Binder.withCleanCallingIdentity(() -> {
                 try (SafeCloseable ignored = IdentityContext.create(identity)) {
-                    return new SoundTriggerSession(mSoundTriggerInternal.attach(client));
+                    return new SoundTriggerSession(
+                            mSoundTriggerInternal.attach(client, moduleProperties));
                 }
             });
         }
 
+        @Override
+        public List<ModuleProperties> listModuleProperties(Identity originatorIdentity) {
+            synchronized (VoiceInteractionManagerServiceStub.this) {
+                enforceIsCurrentVoiceInteractionService();
+            }
+            return mSoundTriggerInternal.listModuleProperties(originatorIdentity);
+        }
+
         // TODO: VI Make sure the caller is the current user or profile
         void startLocalVoiceInteraction(@NonNull final IBinder token,
                 @Nullable String attributionTag, @Nullable Bundle options) {
@@ -1038,7 +1049,8 @@
 
         @Override
         public int startAssistantActivity(@NonNull IBinder token, @NonNull Intent intent,
-                @Nullable String resolvedType, @Nullable String attributionTag) {
+                @Nullable String resolvedType, @NonNull String attributionTag,
+                @NonNull Bundle bundle) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -1049,7 +1061,7 @@
                 final long caller = Binder.clearCallingIdentity();
                 try {
                     return mImpl.startAssistantActivityLocked(attributionTag, callingPid,
-                            callingUid, token, intent, resolvedType);
+                            callingUid, token, intent, resolvedType, bundle);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index ad0e921..96b69f8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -29,7 +29,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
 import android.app.ApplicationExitInfo;
@@ -376,7 +375,8 @@
 
     @GuardedBy("this")
     public int startAssistantActivityLocked(@Nullable String callingFeatureId, int callingPid,
-            int callingUid, IBinder token, Intent intent, String resolvedType) {
+            int callingUid, IBinder token, Intent intent, String resolvedType,
+            @NonNull Bundle bundle) {
         try {
             if (mActiveSession == null || token != mActiveSession.mToken) {
                 Slog.w(TAG, "startAssistantActivity does not match active session");
@@ -388,10 +388,10 @@
             }
             intent = new Intent(intent);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            final ActivityOptions options = ActivityOptions.makeBasic();
-            options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
+            // TODO: make the key public hidden
+            bundle.putInt("android.activity.activityType", ACTIVITY_TYPE_ASSISTANT);
             return mAtm.startAssistantActivity(mComponent.getPackageName(), callingFeatureId,
-                    callingPid, callingUid, intent, resolvedType, options.toBundle(), mUser);
+                    callingPid, callingUid, intent, resolvedType, bundle, mUser);
         } catch (RemoteException e) {
             throw new IllegalStateException("Unexpected remote error", e);
         }