Merge "chore(magnification settings): add logging for settings panel ui interactions" into udc-dev
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index d8f693c..fcff581 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -81,8 +81,6 @@
     name: "platformprotos",
     srcs: [
         ":ipconnectivity-proto-src",
-        ":libstats_atom_enum_protos",
-        ":libstats_atom_message_protos",
         ":libstats_internal_protos",
         ":statsd_internal_protos",
         "cmds/am/proto/instrumentation_data.proto",
diff --git a/TestProtoLibraries.bp b/TestProtoLibraries.bp
index 9e2a64c..2d87841 100644
--- a/TestProtoLibraries.bp
+++ b/TestProtoLibraries.bp
@@ -15,8 +15,6 @@
 java_library_host {
     name: "platformtestprotos",
     srcs: [
-        ":libstats_atom_enum_protos",
-        ":libstats_atom_message_protos",
         ":libstats_internal_protos",
         ":statsd_internal_protos",
     ],
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 59b5978..783e7d3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8359,6 +8359,26 @@
      * from Android {@link android.os.Build.VERSION_CODES#R}, requests to disable camera from
      * legacy device admins targeting SDK version {@link android.os.Build.VERSION_CODES#P} or
      * below will be silently ignored.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the camera disabled
+     * policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
+     * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
+     * successfully set or not. This callback will contain:
+     * <ul>
+     * <li> The policy identifier returned from
+     * {@link DevicePolicyIdentifiers#getIdentifierForUserRestriction(String)} with user restriction
+     * {@link UserManager#DISALLOW_CAMERA}
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+     * reason the policy failed to be set
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with or null if
                      the caller is not a device admin
@@ -9783,6 +9803,27 @@
      * <p>
      * The calling device admin must be a profile owner or device owner. If it is not, a security
      * exception will be thrown.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the persistent preferred
+     * activity policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
+     * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
+     * successfully set or not. This callback will contain:
+     * <ul>
+     * <li> The policy identifier
+     * {@link DevicePolicyIdentifiers#PERSISTENT_PREFERRED_ACTIVITY_POLICY}
+     * <li> The additional policy params bundle, which contains
+     * {@link PolicyUpdateReceiver#EXTRA_INTENT_FILTER} the intent filter the policy applies to
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+     * reason the policy failed to be set
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
      *
      * <p>NOTE: Performs disk I/O and shouldn't be called on the main thread.
      *
@@ -9816,6 +9857,27 @@
      * <p>
      * The calling device admin must be a profile owner. If it is not, a security exception will be
      * thrown.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the persistent preferred
+     * activity policy has been cleared, {@link PolicyUpdateReceiver#onPolicySetResult(Context,
+     * String, Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy
+     * was successfully cleared or not. This callback will contain:
+     * <ul>
+     * <li> The policy identifier
+     * {@link DevicePolicyIdentifiers#PERSISTENT_PREFERRED_ACTIVITY_POLICY}
+     * <li> The additional policy params bundle, which contains
+     * {@link PolicyUpdateReceiver#EXTRA_INTENT_FILTER} the intent filter the policy applies to
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully cleared or the
+     * reason the policy failed to be cleared
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
      *              caller is not a device admin.
@@ -11475,6 +11537,26 @@
      * {@link #getParentProfileInstance(ComponentName)}. To set a restriction globally, call
      * {@link #addUserRestrictionGlobally} instead.
      *
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the user restriction
+     * policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
+     * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
+     * successfully set or not. This callback will contain:
+     * <ul>
+     * <li> The policy identifier returned from
+     * {@link DevicePolicyIdentifiers#getIdentifierForUserRestriction(String)}
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+     * reason the policy failed to be set
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param key   The key of the restriction.
      * @throws SecurityException if {@code admin} is not a device or profile owner and if the caller
@@ -11507,6 +11589,25 @@
      * <p> See the constants in {@link android.os.UserManager} for the list of restrictions that can
      * be enforced device-wide. These constants will also state in their documentation which
      * permission is required to manage the restriction using this API.
+     * <p>
+     * After the user restriction policy has been set,
+     * {@link PolicyUpdateReceiver#onPolicySetResult(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin on whether the policy was successfully set or not.
+     * This callback will contain:
+     * <ul>
+     * <li> The policy identifier returned from
+     * {@link DevicePolicyIdentifiers#getIdentifierForUserRestriction(String)}
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+     * reason the policy failed to be set
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
      *
      * @param key The key of the restriction.
      * @throws SecurityException if {@code admin} is not a device or profile owner and if the
@@ -11544,6 +11645,26 @@
      * above, calling this API will result in clearing any local and global restriction with the
      * specified key that was previously set by the caller.
      *
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the user restriction
+     * policy has been cleared, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
+     * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
+     * successfully cleared or not. This callback will contain:
+     * <ul>
+     * <li> The policy identifier returned from
+     * {@link DevicePolicyIdentifiers#getIdentifierForUserRestriction(String)}
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully cleared or the
+     * reason the policy failed to be cleared
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param key   The key of the restriction.
      * @throws SecurityException if {@code admin} is not a device or profile owner  and if the
@@ -11692,6 +11813,27 @@
      * {@link #getParentProfileInstance(ComponentName)}, where the caller must be the profile owner
      * of an organization-owned managed profile and the package must be a system package. If called
      * on the parent instance, then the package is hidden or unhidden in the personal profile.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the application hidden
+     * policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
+     * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
+     * successfully set or not. This callback will contain:
+     * <ul>
+     * <li> The policy identifier
+     * {@link DevicePolicyIdentifiers#APPLICATION_HIDDEN_POLICY}
+     * <li> The additional policy params bundle, which contains
+     * {@link PolicyUpdateReceiver#EXTRA_PACKAGE_NAME} the package name the policy applies to
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+     * reason the policy failed to be set
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
      *            {@code null} if the caller is not a device admin.
@@ -11731,6 +11873,9 @@
      * of an organization-owned managed profile and the package must be a system package. If called
      * on the parent instance, this will determine whether the package is hidden or unhidden in the
      * personal profile.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the returned policy will be the
+     * current resolved policy rather than the policy set by the calling admin.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
      *            {@code null} if the caller is not a device admin.
@@ -11855,6 +12000,27 @@
      * {@link #getParentProfileInstance(ComponentName)} by the profile owner on an
      * organization-owned device, to restrict accounts that may not be managed on the primary
      * profile.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the account management
+     * disabled policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
+     * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
+     * successfully set or not. This callback will contain:
+     * <ul>
+     * <li> The policy identifier
+     * {@link DevicePolicyIdentifiers#ACCOUNT_MANAGEMENT_DISABLED_POLICY}
+     * <li> The additional policy params bundle, which contains
+     * {@link PolicyUpdateReceiver#EXTRA_ACCOUNT_TYPE} the account type the policy applies to
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+     * reason the policy failed to be set
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
      *              caller is not a device admin.
@@ -11987,6 +12153,28 @@
      * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK_TASK}. See
      * {@link #isAffiliatedUser}.
      * Any package set via this method will be cleared if the user becomes unaffiliated.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the lock task policy has
+     * been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin on whether the policy was successfully set or not.
+     * This callback will contain:
+     * <ul>
+     * <li> The policy identifier {@link DevicePolicyIdentifiers#LOCK_TASK_POLICY}
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+     * reason the policy failed to be set
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, lock task features and lock task
+     * packages are bundled as one policy. A failure to apply one will result in a failure to apply
+     * the other.
      *
      * @param packages The list of packages allowed to enter lock task mode
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
@@ -12016,6 +12204,9 @@
 
     /**
      * Returns the list of packages allowed to start the lock task mode.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the returned policy will be the
+     * current resolved policy rather than the policy set by the calling admin.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
      *              caller is not a device admin.
@@ -12068,6 +12259,28 @@
      * permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK_TASK}. See
      * {@link #isAffiliatedUser}.
      * Any features set using this method are cleared if the user becomes unaffiliated.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the lock task features
+     * policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String, Bundle,
+     * TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
+     * successfully set or not. This callback will contain:
+     * <ul>
+     * <li> The policy identifier {@link DevicePolicyIdentifiers#LOCK_TASK_POLICY}
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+     * reason the policy failed to be set
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, lock task features and lock task
+     * packages are bundled as one policy. A failure to apply one will result in a failure to apply
+     * the other.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
      *               caller is not a device admin.
@@ -12092,6 +12305,9 @@
 
     /**
      * Gets which system features are enabled for LockTask mode.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the returned policy will be the
+     * current resolved policy rather than the policy set by the calling admin.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
      *               caller is not a device admin.
@@ -12577,6 +12793,27 @@
      * profile owner, or by a delegate given the {@link #DELEGATION_BLOCK_UNINSTALL} scope via
      * {@link #setDelegatedScopes} or holders of the permission
      * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APPS_CONTROL}.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the set uninstall blocked
+     * policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
+     * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
+     * successfully set or not. This callback will contain:
+     * <ul>
+     * <li> The policy identifier
+     * {@link DevicePolicyIdentifiers#PACKAGE_UNINSTALL_BLOCKED_POLICY}
+     * <li> The additional policy params bundle, which contains
+     * {@link PolicyUpdateReceiver#EXTRA_PACKAGE_NAME} the package name the policy applies to
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+     * reason the policy failed to be set
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
      *               caller is not a device admin.
@@ -12614,6 +12851,9 @@
      * <strong>Note:</strong> If your app targets Android 11 (API level 30) or higher,
      * this method returns a filtered result. Learn more about how to
      * <a href="/training/basics/intents/package-visibility">manage package visibility</a>.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the returned policy will be the
+     * current resolved policy rather than the policy set by the calling admin.
      *
      * @param admin The name of the admin component whose blocking policy will be checked, or
      *            {@code null} to check whether any admin has blocked the uninstallation. Starting
@@ -15495,7 +15735,7 @@
         throwIfParentInstance("getAllCrossProfilePackages");
         if (mService != null) {
             try {
-                return new ArraySet<>(mService.getAllCrossProfilePackages());
+                return new ArraySet<>(mService.getAllCrossProfilePackages(mContext.getUserId()));
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -15723,6 +15963,25 @@
      * control over apps. User will not be able to clear app data or force-stop packages. When
      * called by a device owner, applies to all users on the device. Packages with user control
      * disabled are exempted from App Standby Buckets.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the user control disabled
+     * packages policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
+     * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
+     * successfully set or not. This callback will contain:
+     * <ul>
+     * <li> The policy identifier
+     * {@link DevicePolicyIdentifiers#USER_CONTROL_DISABLED_PACKAGES_POLICY}
+     * <li> The {@link TargetUser} that this policy relates to
+     * <li> The {@link PolicyUpdateResult}, which will be
+     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+     * reason the policy failed to be set
+     * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+     * </ul>
+     * If there has been a change to the policy,
+     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+     * will contain the reason why the policy changed.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
      *               caller is not a device admin.
@@ -15749,6 +16008,9 @@
      * Returns the list of packages over which user control is disabled by a device or profile
      * owner or holders of the permission
      * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APPS_CONTROL}.
+     * <p>
+     * Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the returned policy will be the
+     * current resolved policy rather than the policy set by the calling admin.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
      *               caller is not a device admin.
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 5345947..d85b2cd 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -206,7 +206,7 @@
      *
      * @hide
      */
-    public abstract List<String> getAllCrossProfilePackages();
+    public abstract List<String> getAllCrossProfilePackages(int userId);
 
     /**
      * Returns the default package names set by the OEM that are allowed to communicate
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9795cab..003e804 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -515,7 +515,7 @@
     void setCrossProfilePackages(in ComponentName admin, in List<String> packageNames);
     List<String> getCrossProfilePackages(in ComponentName admin);
 
-    List<String> getAllCrossProfilePackages();
+    List<String> getAllCrossProfilePackages(int userId);
     List<String> getDefaultCrossProfilePackages();
 
     boolean isManagedKiosk();
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index af5c6dd..2e67225 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -1331,10 +1331,12 @@
 
     /**
      * Enable or disable secure transport for testing. Defaults to enabled.
+     * Should not be used outside of testing.
      *
      * @param enabled true to enable. false to disable.
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
     public void enableSecureTransport(boolean enabled) {
         try {
             mService.enableSecureTransport(enabled);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 379a011..2200af6 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -892,8 +892,9 @@
      */
     private boolean isContentRedirectionAllowedForUser(int incomingUserId) {
         if (MediaStore.AUTHORITY.equals(mAuthority)) {
-            if (mUsersRedirectedToOwnerForMedia.indexOfKey(incomingUserId) >= 0) {
-                return mUsersRedirectedToOwnerForMedia.valueAt(incomingUserId);
+            int incomingUserIdIndex = mUsersRedirectedToOwnerForMedia.indexOfKey(incomingUserId);
+            if (incomingUserIdIndex >= 0) {
+                return mUsersRedirectedToOwnerForMedia.valueAt(incomingUserIdIndex);
             }
 
             // Haven't seen this user yet, look it up
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 307f306..e763e95 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6907,7 +6907,7 @@
      *
      * @hide
      */
-    public static final int FLAG_IGNORE_EPHEMERAL = 0x00000200;
+    public static final int FLAG_IGNORE_EPHEMERAL = 0x80000000;
 
     /**
      * If set, the new activity is not kept in the history stack.  As soon as
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index b5d2f2c..036a4eb 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1698,27 +1698,6 @@
     }
 
     /**
-     * Returns whether the activity supports size changes.
-     * @hide
-     */
-    @SizeChangesSupportMode
-    public int supportsSizeChanges() {
-        if (isChangeEnabled(FORCE_NON_RESIZE_APP)) {
-            return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
-        }
-
-        if (supportsSizeChanges) {
-            return SIZE_CHANGES_SUPPORTED_METADATA;
-        }
-
-        if (isChangeEnabled(FORCE_RESIZE_APP)) {
-            return SIZE_CHANGES_SUPPORTED_OVERRIDE;
-        }
-
-        return SIZE_CHANGES_UNSUPPORTED_METADATA;
-    }
-
-    /**
      * Returns if the activity should never be sandboxed to the activity window bounds.
      * @hide
      */
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index 0993160..3e72d81 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -21,7 +21,14 @@
 
 /** @hide */
 interface IDeviceStateManager {
-    /** Returns the current device state info. */
+    /**
+     * Returns the current device state info. This {@link DeviceStateInfo} object will always
+     * include the list of supported states. If there has been no base state or committed state
+     * provided yet to the system server, this {@link DeviceStateInfo} object will include
+     * {@link DeviceStateManager#INVALID_DEVICE_STATE} for each respectively.
+     *
+     * This method should not be used to notify callback clients.
+     */
     DeviceStateInfo getDeviceStateInfo();
 
     /**
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index 6b7d8c3..7ae88b8 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -22,6 +22,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -233,6 +234,31 @@
         dest.writeLong(colorSampleDuration);
     }
 
+    @Override
+    public String toString() {
+        return "BrightnessChangeEvent{"
+                + "brightness: " + brightness
+                + ", timeStamp: " + timeStamp
+                + ", packageName: " + packageName
+                + ", userId: " + userId
+                + ", uniqueDisplayId: " + uniqueDisplayId
+                + ", luxValues: " + Arrays.toString(luxValues)
+                + ", luxTimestamps: " + Arrays.toString(luxTimestamps)
+                + ", batteryLevel: " + batteryLevel
+                + ", powerBrightnessFactor: " + powerBrightnessFactor
+                + ", nightMode: " + nightMode
+                + ", colorTemperature: " + colorTemperature
+                + ", reduceBrightColors: " + reduceBrightColors
+                + ", reduceBrightColorsStrength: " + reduceBrightColorsStrength
+                + ", reduceBrightColorsOffset: " + reduceBrightColorsOffset
+                + ", lastBrightness: " + lastBrightness
+                + ", isDefaultBrightnessConfig: " + isDefaultBrightnessConfig
+                + ", isUserSetBrightness: " + isUserSetBrightness
+                + ", colorValueBuckets: " + Arrays.toString(colorValueBuckets)
+                + ", colorSampleDuration: " + colorSampleDuration
+                + "}";
+    }
+
     /** @hide */
     public static class Builder {
         private float mBrightness;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fd5e206..5c79f69 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3036,7 +3036,9 @@
 
         public void destroy() {
             try {
-                if (!mArray.isClosed()) {
+                // If this process is the system server process, mArray is the same object as
+                // the memory int array kept inside SettingsProvider, so skipping the close()
+                if (!Settings.isInSystemServer() && !mArray.isClosed()) {
                     mArray.close();
                 }
             } catch (IOException e) {
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 5f7486a..d04ff8e 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -123,47 +123,50 @@
      */
     public static final int PICK_REASON_UNKNOWN = 0;
     /**
-     * This dataset is picked because of autofill provider detection was chosen.
+     * This dataset is picked because pcc wasn't enabled.
      * @hide
      */
-    public static final int PICK_REASON_AUTOFILL_PROVIDER_DETECTION = 1;
+    public static final int PICK_REASON_NO_PCC = 1;
+    /**
+     * This dataset is picked because provider gave this dataset.
+     * @hide
+     */
+    public static final int PICK_REASON_PROVIDER_DETECTION_ONLY = 2;
+    /**
+     * This dataset is picked because provider detection was preferred. However, provider also made
+     * this dataset available for PCC detected types, so they could've been picked up by PCC
+     * detection. This however doesn't imply that this dataset would've been chosen for sure. For
+     * eg, if PCC Detection was preferred, and PCC detected other field types, which wasn't
+     * applicable to this dataset, it wouldn't have been shown.
+     * @hide
+     */
+    public static final int PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC = 3;
     /**
      * This dataset is picked because of PCC detection was chosen.
      * @hide
      */
-    public static final int PICK_REASON_PCC_DETECTION = 2;
+    public static final int PICK_REASON_PCC_DETECTION_ONLY = 4;
     /**
-     * This dataset is picked because of Framework detection was chosen.
+     * This dataset is picked because of PCC Detection was preferred. However, Provider also gave
+     * this dataset, so if PCC wasn't enabled, this dataset would've been eligible anyway.
      * @hide
      */
-    public static final int PICK_REASON_FRAMEWORK_DETECTION = 3;
-    /**
-     * This dataset is picked because of Autofill Provider being a fallback.
-     * @hide
-     */
-    public static final int PICK_REASON_AUTOFILL_PROVIDER_FALLBACK = 4;
-    /**
-     * This dataset is picked because of PCC detection being a fallback.
-     * @hide
-     */
-    public static final int PICK_REASON_PCC_DETECTION_FALLBACK = 5;
-    /**
-     * This dataset is picked because of Framework detection being a fallback.
-     * @hide
-     */
-    public static final int PICK_REASON_FRAMEWORK_FALLBACK = 6;
+    public static final int PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER = 5;
 
+    /**
+     * Reason why the dataset was eligible for autofill.
+     * @hide
+     */
     @IntDef(prefix = { "PICK_REASON_" }, value = {
             PICK_REASON_UNKNOWN,
-            PICK_REASON_AUTOFILL_PROVIDER_DETECTION,
-            PICK_REASON_PCC_DETECTION,
-            PICK_REASON_FRAMEWORK_DETECTION,
-            PICK_REASON_AUTOFILL_PROVIDER_FALLBACK,
-            PICK_REASON_PCC_DETECTION_FALLBACK,
-            PICK_REASON_FRAMEWORK_FALLBACK,
+            PICK_REASON_NO_PCC,
+            PICK_REASON_PROVIDER_DETECTION_ONLY,
+            PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC,
+            PICK_REASON_PCC_DETECTION_ONLY,
+            PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER,
     })
     @Retention(RetentionPolicy.SOURCE)
-    @interface DatasetEligibleReason{}
+    public @interface DatasetEligibleReason{}
 
     private @DatasetEligibleReason int mEligibleReason;
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1850462..e95ba79 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1216,6 +1216,37 @@
             "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE";
 
     /**
+     * Application level {@link android.content.pm.PackageManager.Property PackageManager
+     * .Property} for an app to inform the system that the app should be opted-out from the
+     * compatibility overrides that change the resizability of the app.
+     *
+     * <p>When these compat overrides are enabled they force the packages they are applied to to be
+     * resizable / unresizable. If the app is forced to be resizable this won't change whether
+     * the app can be put into multi-windowing mode, but allow the app to resize without going into
+     * size-compat mode when the window container resizes, such as display size change or screen
+     * rotation.
+     *
+     * <p>Setting this property to {@code false} informs the system that the app must be
+     * opted-out from the compatibility treatment even if the device manufacturer has opted the app
+     * into the treatment.
+     *
+     * <p>Not setting this property at all, or setting this property to {@code true} has no effect.
+     *
+     * <p><b>Syntax:</b>
+     * <pre>
+     * &lt;application&gt;
+     *   &lt;property
+     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"
+     *     android:value="true|false"/&gt;
+     * &lt;/application&gt;
+     * </pre>
+     * @hide
+     */
+    // TODO(b/280052089): Make this public API.
+    String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES =
+            "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
+
+    /**
      * @hide
      */
     public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index b67969e..ba54686 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -275,6 +275,14 @@
     public static final boolean DEFAULT_AUTOFILL_PCC_CLASSIFICATION_ENABLED = false;
     // END AUTOFILL PCC CLASSIFICATION FLAGS DEFAULTS
 
+    // AUTOFILL FOR ALL APPS DEFAULTS
+    private static final boolean DEFAULT_AFAA_ON_UNIMPORTANT_VIEW_ENABLED = true;
+    private static final boolean DEFAULT_AFAA_ON_IMPORTANT_VIEW_ENABLED = true;
+    private static final String DEFAULT_AFAA_DENYLIST = "";
+    private static final String DEFAULT_AFAA_ALLOWLIST = "";
+    private static final String DEFAULT_AFAA_NON_AUTOFILLABLE_IME_ACTIONS = "2,3,4";
+    private static final boolean DEFAULT_AFAA_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES = true;
+    private static final boolean DEFAULT_AFAA_SHOULD_ENABLE_MULTILINE_FILTER = true;
 
     private AutofillFeatureFlags() {};
 
@@ -330,7 +338,8 @@
     public static boolean isTriggerFillRequestOnUnimportantViewEnabled() {
         return DeviceConfig.getBoolean(
             DeviceConfig.NAMESPACE_AUTOFILL,
-            DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_UNIMPORTANT_VIEW, false);
+            DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_UNIMPORTANT_VIEW,
+            DEFAULT_AFAA_ON_UNIMPORTANT_VIEW_ENABLED);
     }
 
     /**
@@ -341,7 +350,8 @@
     public static boolean isTriggerFillRequestOnFilteredImportantViewsEnabled() {
         return DeviceConfig.getBoolean(
             DeviceConfig.NAMESPACE_AUTOFILL,
-            DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_FILTERED_IMPORTANT_VIEWS, false);
+            DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_FILTERED_IMPORTANT_VIEWS,
+            DEFAULT_AFAA_ON_IMPORTANT_VIEW_ENABLED);
     }
 
     /**
@@ -352,7 +362,8 @@
     public static boolean shouldEnableAutofillOnAllViewTypes(){
         return DeviceConfig.getBoolean(
             DeviceConfig.NAMESPACE_AUTOFILL,
-            DEVICE_CONFIG_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES, false);
+            DEVICE_CONFIG_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES,
+            DEFAULT_AFAA_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES);
     }
 
     /**
@@ -363,7 +374,9 @@
      */
     public static Set<String> getNonAutofillableImeActionIdSetFromFlag() {
         final String mNonAutofillableImeActions = DeviceConfig.getString(
-                DeviceConfig.NAMESPACE_AUTOFILL, DEVICE_CONFIG_NON_AUTOFILLABLE_IME_ACTION_IDS, "");
+                DeviceConfig.NAMESPACE_AUTOFILL,
+                DEVICE_CONFIG_NON_AUTOFILLABLE_IME_ACTION_IDS,
+                DEFAULT_AFAA_NON_AUTOFILLABLE_IME_ACTIONS);
         return new ArraySet<>(Arrays.asList(mNonAutofillableImeActions.split(",")));
     }
 
@@ -378,7 +391,8 @@
     public static String getDenylistStringFromFlag() {
         return DeviceConfig.getString(
             DeviceConfig.NAMESPACE_AUTOFILL,
-            DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW, "");
+            DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW,
+            DEFAULT_AFAA_DENYLIST);
     }
 
     /**
@@ -389,7 +403,8 @@
     public static String getAllowlistStringFromFlag() {
         return DeviceConfig.getString(
             DeviceConfig.NAMESPACE_AUTOFILL,
-            DEVICE_CONFIG_PACKAGE_AND_ACTIVITY_ALLOWLIST_FOR_TRIGGERING_FILL_REQUEST, "");
+            DEVICE_CONFIG_PACKAGE_AND_ACTIVITY_ALLOWLIST_FOR_TRIGGERING_FILL_REQUEST,
+            DEFAULT_AFAA_ALLOWLIST);
     }
     /**
      * Whether include all views that have autofill type not none in assist structure.
@@ -422,7 +437,8 @@
     public static boolean shouldEnableMultilineFilter() {
         return DeviceConfig.getBoolean(
             DeviceConfig.NAMESPACE_AUTOFILL,
-            DEVICE_CONFIG_MULTILINE_FILTER_ENABLED, false);
+            DEVICE_CONFIG_MULTILINE_FILTER_ENABLED,
+            DEFAULT_AFAA_SHOULD_ENABLE_MULTILINE_FILTER);
     }
 
     // START AUTOFILL PCC CLASSIFICATION FUNCTIONS
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 2ef7419..ea75076 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -468,7 +468,8 @@
             COMMIT_REASON_ACTIVITY_FINISHED,
             COMMIT_REASON_VIEW_COMMITTED,
             COMMIT_REASON_VIEW_CLICKED,
-            COMMIT_REASON_VIEW_CHANGED
+            COMMIT_REASON_VIEW_CHANGED,
+            COMMIT_REASON_SESSION_DESTROYED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AutofillCommitReason {}
@@ -507,6 +508,12 @@
      * @hide
      */
     public static final int COMMIT_REASON_VIEW_CHANGED = 4;
+    /**
+     * Autofill context was committed because of the session was destroyed.
+     *
+     * @hide
+     */
+    public static final int COMMIT_REASON_SESSION_DESTROYED = 5;
 
     /**
      * Makes an authentication id from a request id and a dataset id.
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index ace8451..2b39bb4 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -165,9 +165,11 @@
 import java.util.stream.Collectors;
 
 /**
- * The Chooser Activity handles intent resolution specifically for sharing intents -
- * for example, those generated by @see android.content.Intent#createChooser(Intent, CharSequence).
+ * This is the legacy ChooserActivity and is not expected to be invoked, it's only here because
+ * MediaAppSelectorActivity is still depending on it. The actual chooser used by the system is
+ * at packages/modules/IntentResolver/java/src/com/android/intentresolver/ChooserActivity.java
  *
+ * The migration to the new package will be completed in a later release.
  */
 public class ChooserActivity extends ResolverActivity implements
         ChooserListAdapter.ChooserListCommunicator,
diff --git a/core/proto/android/companion/telecom.proto b/core/proto/android/companion/telecom.proto
index b90067d..700baa1 100644
--- a/core/proto/android/companion/telecom.proto
+++ b/core/proto/android/companion/telecom.proto
@@ -42,6 +42,9 @@
       ONGOING = 2;
       ON_HOLD = 3;
       RINGING_SILENCED = 4;
+      AUDIO_PROCESSING = 5;
+      RINGING_SIMULATED = 6;
+      DISCONNECTED = 7;
     }
     Status status = 3;
 
@@ -89,8 +92,6 @@
     END = 6;
     PUT_ON_HOLD = 7;
     TAKE_OFF_HOLD = 8;
-    REJECT_AND_BLOCK = 9;
-    IGNORE = 10;
   }
 
   // The list of active calls.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 63afc34..2f9f6ae 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7692,22 +7692,6 @@
                  android:defaultToDeviceProtectedStorage="true"
                  android:forceQueryable="true"
                  android:directBootAware="true">
-        <activity android:name="com.android.internal.app.ChooserActivity"
-                android:theme="@style/Theme.DeviceDefault.Chooser"
-                android:finishOnCloseSystemDialogs="true"
-                android:excludeFromRecents="true"
-                android:documentLaunchMode="never"
-                android:relinquishTaskIdentity="true"
-                android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
-                android:process=":ui"
-                android:exported="true"
-                android:visibleToInstantApps="true">
-            <intent-filter android:priority="100">
-                <action android:name="android.intent.action.CHOOSER" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.VOICE" />
-            </intent-filter>
-        </activity>
         <activity android:name="com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity"
                   android:exported="false"
                   android:theme="@style/Theme.DeviceDefault.Dialog.Alert.DayNight"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 56616cb..ed3aadd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -51,6 +51,8 @@
 import com.android.wm.shell.common.ScreenshotUtils;
 import com.android.wm.shell.common.SurfaceUtils;
 
+import java.util.function.Consumer;
+
 /**
  * Handles split decor like showing resizing hint for a specific split.
  */
@@ -251,7 +253,7 @@
     }
 
     /** Stops showing resizing hint. */
-    public void onResized(SurfaceControl.Transaction t, Runnable animFinishedCallback) {
+    public void onResized(SurfaceControl.Transaction t, Consumer<Boolean> animFinishedCallback) {
         if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) {
             mScreenshotAnimator.cancel();
         }
@@ -281,7 +283,7 @@
                     mScreenshot = null;
 
                     if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
-                        animFinishedCallback.run();
+                        animFinishedCallback.accept(true);
                     }
                 }
             });
@@ -313,12 +315,12 @@
             }
         }
         if (mShown) {
-            fadeOutDecor(animFinishedCallback);
+            fadeOutDecor(()-> animFinishedCallback.accept(true));
         } else {
             // Decor surface is hidden so release it directly.
             releaseDecor(t);
             if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
-                animFinishedCallback.run();
+                animFinishedCallback.accept(false);
             }
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 2832c55..9a2ec15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -717,6 +717,10 @@
         return bounds.width() > bounds.height();
     }
 
+    public boolean isDensityChanged(int densityDpi) {
+        return mDensity != densityDpi;
+    }
+
     /**
      * Return if this layout is landscape.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index 042721c9..0289da9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -78,8 +78,15 @@
         return taskInfo != null ? getPackageName(taskInfo.baseIntent) : null;
     }
 
-    /** Returns true if they are the same package. */
-    public static boolean samePackage(String packageName1, String packageName2) {
-        return packageName1 != null && packageName1.equals(packageName2);
+    /** Retrieve user id from a taskId */
+    public static int getUserId(int taskId, ShellTaskOrganizer taskOrganizer) {
+        final ActivityManager.RunningTaskInfo taskInfo = taskOrganizer.getRunningTaskInfo(taskId);
+        return taskInfo != null ? taskInfo.userId : -1;
+    }
+
+    /** Returns true if package names and user ids match. */
+    public static boolean samePackage(String packageName1, String packageName2,
+            int userId1, int userId2) {
+        return (packageName1 != null && packageName1.equals(packageName2)) && (userId1 == userId2);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index be0288e..4980e49 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -201,6 +201,7 @@
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
             SyncTransactionQueue syncQueue,
+            Transitions transitions,
             Optional<DesktopModeController> desktopModeController,
             Optional<DesktopTasksController> desktopTasksController,
             Optional<SplitScreenController> splitScreenController) {
@@ -212,6 +213,7 @@
                     taskOrganizer,
                     displayController,
                     syncQueue,
+                    transitions,
                     desktopModeController,
                     desktopTasksController,
                     splitScreenController);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index df94b41..fb08c87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -266,6 +266,7 @@
         final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
         final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
                 ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
+        final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
 
         if (isTask) {
             final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
@@ -273,14 +274,14 @@
         } else if (isShortcut) {
             final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
             final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
-            final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
             mStarter.startShortcut(packageName, id, position, opts, user);
         } else {
             final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
             // Put BAL flags to avoid activity start aborted.
             opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
             opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
-            mStarter.startIntent(launchIntent, null /* fillIntent */, position, opts);
+            mStarter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */,
+                    position, opts);
         }
     }
 
@@ -334,8 +335,8 @@
         void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options);
         void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
                 @Nullable Bundle options, UserHandle user);
-        void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
-                @Nullable Bundle options);
+        void startIntent(PendingIntent intent, int userId, Intent fillInIntent,
+                @SplitPosition int position, @Nullable Bundle options);
         void enterSplitScreen(int taskId, boolean leftOrTop);
 
         /**
@@ -379,8 +380,8 @@
         }
 
         @Override
-        public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent, int position,
-                @Nullable Bundle options) {
+        public void startIntent(PendingIntent intent, int userId, @Nullable Intent fillInIntent,
+                int position, @Nullable Bundle options) {
             try {
                 intent.send(mContext, 0, fillInIntent, null, null, null, options);
             } catch (PendingIntent.CanceledException e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index f8e1435..9e6bd47 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -946,10 +946,15 @@
                     mPipBoundsState.getDisplayBounds().right,
                     mPipBoundsState.getDisplayBounds().bottom);
             mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG, rect);
+            updatePipPositionForKeepClearAreas();
         } else {
             mPipBoundsState.removeNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG);
+            // postpone moving in response to hide of Launcher in case there's another change
+            mMainExecutor.removeCallbacks(mMovePipInResponseToKeepClearAreasChangeCallback);
+            mMainExecutor.executeDelayed(
+                    mMovePipInResponseToKeepClearAreasChangeCallback,
+                    PIP_KEEP_CLEAR_AREAS_DELAY);
         }
-        updatePipPositionForKeepClearAreas();
     }
 
     private void setLauncherAppIconSize(int iconSizePx) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index f819bee..c414e70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -73,8 +73,8 @@
     /**
      * Starts an activity in a stage.
      */
-    oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int position,
-            in Bundle options, in InstanceId instanceId) = 9;
+    oneway void startIntent(in PendingIntent intent, int userId, in Intent fillInIntent,
+            int position, in Bundle options, in InstanceId instanceId) = 9;
 
     /**
      * Starts tasks simultaneously in one transition.
@@ -86,8 +86,8 @@
     /**
      * Starts a pair of intent and task in one transition.
      */
-    oneway void startIntentAndTask(in PendingIntent pendingIntent, in Bundle options1, int taskId,
-            in Bundle options2, int sidePosition, float splitRatio,
+    oneway void startIntentAndTask(in PendingIntent pendingIntent, int userId1, in Bundle options1,
+            int taskId, in Bundle options2, int sidePosition, float splitRatio,
             in RemoteTransition remoteTransition, in InstanceId instanceId) = 16;
 
     /**
@@ -107,7 +107,7 @@
     /**
      * Starts a pair of intent and task using legacy transition system.
      */
-    oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
+    oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent, int userId1,
             in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio,
             in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 12;
 
@@ -121,18 +121,18 @@
     /**
      * Start a pair of intents using legacy transition system.
      */
-    oneway void startIntentsWithLegacyTransition(in PendingIntent pendingIntent1,
+    oneway void startIntentsWithLegacyTransition(in PendingIntent pendingIntent1, int userId1,
             in ShortcutInfo shortcutInfo1, in Bundle options1, in PendingIntent pendingIntent2,
-            in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition, float splitRatio,
-            in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 18;
+            int userId2, in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition,
+            float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 18;
 
     /**
      * Start a pair of intents in one transition.
      */
-    oneway void startIntents(in PendingIntent pendingIntent1, in ShortcutInfo shortcutInfo1,
-            in Bundle options1, in PendingIntent pendingIntent2, in ShortcutInfo shortcutInfo2,
-            in Bundle options2, int splitPosition, float splitRatio,
-            in RemoteTransition remoteTransition, in InstanceId instanceId) = 19;
+    oneway void startIntents(in PendingIntent pendingIntent1, int userId1,
+            in ShortcutInfo shortcutInfo1, in Bundle options1, in PendingIntent pendingIntent2,
+            int userId2, in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition,
+            float splitRatio, in RemoteTransition remoteTransition, in InstanceId instanceId) = 19;
 
     /**
      * Blocking call that notifies and gets additional split-screen targets when entering
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index c654c1b..34701f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -502,7 +502,8 @@
         if (options == null) options = new Bundle();
         final ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
 
-        if (samePackage(packageName, getPackageName(reverseSplitPosition(position)))) {
+        if (samePackage(packageName, getPackageName(reverseSplitPosition(position)),
+                user.getIdentifier(), getUserId(reverseSplitPosition(position)))) {
             if (supportMultiInstancesSplit(packageName)) {
                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
@@ -531,7 +532,9 @@
 
         final String packageName1 = shortcutInfo.getPackage();
         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
-        if (samePackage(packageName1, packageName2)) {
+        final int userId1 = shortcutInfo.getUserId();
+        final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
+        if (samePackage(packageName1, packageName2, userId1, userId2)) {
             if (supportMultiInstancesSplit(shortcutInfo.getPackage())) {
                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
@@ -558,7 +561,9 @@
         // NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
         //       recents that hasn't launched and is not being organized
         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
-        if (samePackage(packageName1, packageName2)) {
+        final int userId1 = shortcutInfo.getUserId();
+        final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
+        if (samePackage(packageName1, packageName2, userId1, userId2)) {
             if (supportMultiInstancesSplit(packageName1)) {
                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
@@ -578,23 +583,24 @@
     }
 
     /**
-     * See {@link #startIntent(PendingIntent, Intent, int, Bundle)}
+     * See {@link #startIntent(PendingIntent, int, Intent, int, Bundle)}
      * @param instanceId to be used by {@link SplitscreenEventLogger}
      */
-    public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
+    public void startIntent(PendingIntent intent, int userId, @Nullable Intent fillInIntent,
             @SplitPosition int position, @Nullable Bundle options, @NonNull InstanceId instanceId) {
         mStageCoordinator.onRequestToSplit(instanceId, ENTER_REASON_LAUNCHER);
-        startIntent(intent, fillInIntent, position, options);
+        startIntent(intent, userId, fillInIntent, position, options);
     }
 
-    private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
+    private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
             @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
             InstanceId instanceId) {
         Intent fillInIntent = null;
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
-        if (samePackage(packageName1, packageName2)) {
+        final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
+        if (samePackage(packageName1, packageName2, userId1, userId2)) {
             if (supportMultiInstancesSplit(packageName1)) {
                 fillInIntent = new Intent();
                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
@@ -611,15 +617,17 @@
                 options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId);
     }
 
-    private void startIntentAndTask(PendingIntent pendingIntent, @Nullable Bundle options1,
-            int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
-            float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+    private void startIntentAndTask(PendingIntent pendingIntent, int userId1,
+            @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
+            @SplitPosition int splitPosition, float splitRatio,
+            @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         Intent fillInIntent = null;
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
         // NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
         //       recents that hasn't launched and is not being organized
         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
-        if (samePackage(packageName1, packageName2)) {
+        final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
+        if (samePackage(packageName1, packageName2, userId1, userId2)) {
             if (supportMultiInstancesSplit(packageName1)) {
                 fillInIntent = new Intent();
                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
@@ -639,16 +647,16 @@
                 options2, splitPosition, splitRatio, remoteTransition, instanceId);
     }
 
-    private void startIntentsWithLegacyTransition(PendingIntent pendingIntent1,
+    private void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
             @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
-            PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2,
+            PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
             @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
             RemoteAnimationAdapter adapter, InstanceId instanceId) {
         Intent fillInIntent1 = null;
         Intent fillInIntent2 = null;
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
         final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
-        if (samePackage(packageName1, packageName2)) {
+        if (samePackage(packageName1, packageName2, userId1, userId2)) {
             if (supportMultiInstancesSplit(packageName1)) {
                 fillInIntent1 = new Intent();
                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
@@ -668,16 +676,16 @@
                 splitPosition, splitRatio, adapter, instanceId);
     }
 
-    private void startIntents(PendingIntent pendingIntent1,
+    private void startIntents(PendingIntent pendingIntent1, int userId1,
             @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
-            PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2,
+            PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
             @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         Intent fillInIntent1 = null;
         Intent fillInIntent2 = null;
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
         final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
-        if (samePackage(packageName1, packageName2)) {
+        if (samePackage(packageName1, packageName2, userId1, userId2)) {
             if (supportMultiInstancesSplit(packageName1)) {
                 fillInIntent1 = new Intent();
                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
@@ -698,7 +706,7 @@
     }
 
     @Override
-    public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
+    public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent,
             @SplitPosition int position, @Nullable Bundle options) {
         // Flag this as a no-user-action launch to prevent sending user leaving event to the current
         // top activity since it's going to be put into another side of the split. This prevents the
@@ -708,7 +716,8 @@
 
         final String packageName1 = SplitScreenUtils.getPackageName(intent);
         final String packageName2 = getPackageName(reverseSplitPosition(position));
-        if (SplitScreenUtils.samePackage(packageName1, packageName2)) {
+        final int userId2 = getUserId(reverseSplitPosition(position));
+        if (samePackage(packageName1, packageName2, userId1, userId2)) {
             if (supportMultiInstancesSplit(packageName1)) {
                 // To prevent accumulating large number of instances in the background, reuse task
                 // in the background with priority.
@@ -761,6 +770,24 @@
         return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : null;
     }
 
+    /** Retrieve user id of a specific split position if split screen is activated, otherwise
+     *  returns the user id of the top running task. */
+    private int getUserId(@SplitPosition int position) {
+        ActivityManager.RunningTaskInfo taskInfo;
+        if (isSplitScreenVisible()) {
+            taskInfo = getTaskInfo(position);
+        } else {
+            taskInfo = mRecentTasksOptional
+                    .map(recentTasks -> recentTasks.getTopRunningTask())
+                    .orElse(null);
+            if (!isValidToSplit(taskInfo)) {
+                return -1;
+            }
+        }
+
+        return taskInfo != null ? taskInfo.userId : -1;
+    }
+
     @VisibleForTesting
     boolean supportMultiInstancesSplit(String packageName) {
         if (packageName != null) {
@@ -1069,14 +1096,14 @@
         }
 
         @Override
-        public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
+        public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
                 Bundle options1, int taskId, Bundle options2, int splitPosition, float splitRatio,
                 RemoteAnimationAdapter adapter, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController,
                     "startIntentAndTaskWithLegacyTransition", (controller) ->
                             controller.startIntentAndTaskWithLegacyTransition(pendingIntent,
-                                    options1, taskId, options2, splitPosition, splitRatio, adapter,
-                                    instanceId));
+                                    userId1, options1, taskId, options2, splitPosition, splitRatio,
+                                    adapter, instanceId));
         }
 
         @Override
@@ -1102,13 +1129,14 @@
         }
 
         @Override
-        public void startIntentAndTask(PendingIntent pendingIntent, @Nullable Bundle options1,
-                int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
-                float splitRatio, @Nullable RemoteTransition remoteTransition,
-                InstanceId instanceId) {
+        public void startIntentAndTask(PendingIntent pendingIntent, int userId1,
+                @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
+                @SplitPosition int splitPosition, float splitRatio,
+                @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startIntentAndTask",
-                    (controller) -> controller.startIntentAndTask(pendingIntent, options1, taskId,
-                            options2, splitPosition, splitRatio, remoteTransition, instanceId));
+                    (controller) -> controller.startIntentAndTask(pendingIntent, userId1, options1,
+                            taskId, options2, splitPosition, splitRatio, remoteTransition,
+                            instanceId));
         }
 
         @Override
@@ -1122,29 +1150,29 @@
         }
 
         @Override
-        public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1,
+        public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
                 @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
-                PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2,
+                PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
                 @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
                 RemoteAnimationAdapter adapter, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startIntentsWithLegacyTransition",
                     (controller) ->
-                        controller.startIntentsWithLegacyTransition(pendingIntent1, shortcutInfo1,
-                                options1, pendingIntent2, shortcutInfo2, options2, splitPosition,
-                                splitRatio, adapter, instanceId)
+                        controller.startIntentsWithLegacyTransition(pendingIntent1, userId1,
+                                shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2,
+                                options2, splitPosition, splitRatio, adapter, instanceId)
                     );
         }
 
         @Override
-        public void startIntents(PendingIntent pendingIntent1, @Nullable ShortcutInfo shortcutInfo1,
-                @Nullable Bundle options1, PendingIntent pendingIntent2,
-                @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
-                @SplitPosition int splitPosition, float splitRatio,
+        public void startIntents(PendingIntent pendingIntent1, int userId1,
+                @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
+                PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
+                @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
                 @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startIntents",
                     (controller) ->
-                            controller.startIntents(pendingIntent1, shortcutInfo1,
-                                    options1, pendingIntent2, shortcutInfo2, options2,
+                            controller.startIntents(pendingIntent1, userId1, shortcutInfo1,
+                                    options1, pendingIntent2, userId2, shortcutInfo2, options2,
                                     splitPosition, splitRatio, remoteTransition, instanceId)
             );
         }
@@ -1158,11 +1186,11 @@
         }
 
         @Override
-        public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
+        public void startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position,
                 @Nullable Bundle options, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startIntent",
-                    (controller) -> controller.startIntent(intent, fillInIntent, position, options,
-                            instanceId));
+                    (controller) -> controller.startIntent(intent, userId, fillInIntent, position,
+                            options, instanceId));
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index ee36d52..c15ab0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -208,11 +208,13 @@
                 mAnimations.add(va);
 
                 decor.setScreenshotIfNeeded(change.getSnapshot(), startTransaction);
-                decor.onResized(startTransaction, () -> {
-                    mTransitions.getMainExecutor().execute(() -> {
-                        mAnimations.remove(va);
-                        onFinish(null /* wct */, null /* wctCB */);
-                    });
+                decor.onResized(startTransaction, animated -> {
+                    mAnimations.remove(va);
+                    if (animated) {
+                        mTransitions.getMainExecutor().execute(() -> {
+                            onFinish(null /* wct */, null /* wctCB */);
+                        });
+                    }
                 });
             }
         }
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 fcea9ad..ebf464c 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
@@ -2175,6 +2175,14 @@
             return;
         }
         mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId));
+
+        if (mSplitLayout != null && mSplitLayout.isDensityChanged(newConfig.densityDpi)
+                && mMainStage.isActive()
+                && mSplitLayout.updateConfiguration(newConfig)
+                && ENABLE_SHELL_TRANSITIONS) {
+            mSplitLayout.update(null /* t */);
+            onLayoutSizeChanged(mSplitLayout);
+        }
     }
 
     void updateSurfaces(SurfaceControl.Transaction transaction) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index fe2faaf..2e7fca3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -135,6 +135,21 @@
     }
 
     /**
+     * Looks through the pending transitions for a opening transaction that matches the provided
+     * `taskView`.
+     * @param taskView the pending transition should be for this.
+     */
+    private PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) {
+        for (int i = mPending.size() - 1; i >= 0; --i) {
+            if (mPending.get(i).mTaskView != taskView) continue;
+            if (TransitionUtil.isOpeningType(mPending.get(i).mType)) {
+                return mPending.get(i);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Looks through the pending transitions for one matching `taskView`.
      * @param taskView the pending transition should be for this.
      * @param type the type of transition it's looking for
@@ -149,6 +164,19 @@
         return null;
     }
 
+    /**
+     * Returns all the pending transitions for a given `taskView`.
+     * @param taskView the pending transition should be for this.
+     */
+    ArrayList<PendingTransition> findAllPending(TaskViewTaskController taskView) {
+        ArrayList<PendingTransition> list = new ArrayList<>();
+        for (int i = mPending.size() - 1; i >= 0; --i) {
+            if (mPending.get(i).mTaskView != taskView) continue;
+            list.add(mPending.get(i));
+        }
+        return list;
+    }
+
     private PendingTransition findPending(IBinder claimed) {
         for (int i = 0; i < mPending.size(); ++i) {
             if (mPending.get(i).mClaimed != claimed) continue;
@@ -249,9 +277,10 @@
             // Task view isn't visible, the bounds will next visibility update.
             return;
         }
-        if (hasPending()) {
-            // There is already a transition in-flight, the window bounds will be set in
-            // prepareOpenAnimation.
+        PendingTransition pendingOpen = findPendingOpeningTransition(taskView);
+        if (pendingOpen != null) {
+            // There is already an opening transition in-flight, the window bounds will be
+            // set in prepareOpenAnimation (via the window crop) if needed.
             return;
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
index ef0c7a8..6d37e58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
@@ -235,11 +235,20 @@
     public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
             TransitionInfo info, SurfaceControl.Transaction t,
             @Nullable ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
+        return newTarget(change, order, false /* forceTranslucent */, info, t, leashMap);
+    }
+
+    /**
+     * Creates a new RemoteAnimationTarget from the provided change info
+     */
+    public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
+            boolean forceTranslucent, TransitionInfo info, SurfaceControl.Transaction t,
+            @Nullable ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
         final SurfaceControl leash = createLeash(info, change, order, t);
         if (leashMap != null) {
             leashMap.put(change.getLeash(), leash);
         }
-        return newTarget(change, order, leash);
+        return newTarget(change, order, leash, forceTranslucent);
     }
 
     /**
@@ -247,6 +256,14 @@
      */
     public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
             SurfaceControl leash) {
+        return newTarget(change, order, leash, false /* forceTranslucent */);
+    }
+
+    /**
+     * Creates a new RemoteAnimationTarget from the provided change and leash
+     */
+    public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
+            SurfaceControl leash, boolean forceTranslucent) {
         if (isDividerBar(change)) {
             return getDividerTarget(change, leash);
         }
@@ -276,7 +293,7 @@
                 // TODO: once we can properly sync transactions across process,
                 // then get rid of this leash.
                 leash,
-                (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0,
+                forceTranslucent || (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0,
                 null,
                 // TODO(shell-transitions): we need to send content insets? evaluate how its used.
                 new Rect(0, 0, 0, 0),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 54babce..9fd57d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -104,6 +104,7 @@
     private final InputMonitorFactory mInputMonitorFactory;
     private TaskOperations mTaskOperations;
     private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
+    private final Transitions mTransitions;
 
     private Optional<SplitScreenController> mSplitScreenController;
 
@@ -118,6 +119,7 @@
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
             SyncTransactionQueue syncQueue,
+            Transitions transitions,
             Optional<DesktopModeController> desktopModeController,
             Optional<DesktopTasksController> desktopTasksController,
             Optional<SplitScreenController> splitScreenController) {
@@ -128,6 +130,7 @@
                 taskOrganizer,
                 displayController,
                 syncQueue,
+                transitions,
                 desktopModeController,
                 desktopTasksController,
                 splitScreenController,
@@ -144,6 +147,7 @@
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
             SyncTransactionQueue syncQueue,
+            Transitions transitions,
             Optional<DesktopModeController> desktopModeController,
             Optional<DesktopTasksController> desktopTasksController,
             Optional<SplitScreenController> splitScreenController,
@@ -158,6 +162,7 @@
         mDisplayController = displayController;
         mSplitScreenController = splitScreenController;
         mSyncQueue = syncQueue;
+        mTransitions = transitions;
         mDesktopModeController = desktopModeController;
         mDesktopTasksController = desktopTasksController;
 
@@ -818,7 +823,8 @@
         } else {
             windowDecoration.createResizeVeil();
             return new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration,
-                    mDisplayController, disallowedAreaForEndBounds, mDragStartListener);
+                    mDisplayController, disallowedAreaForEndBounds, mDragStartListener,
+                    mTransitions);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
index b785ef0..8277109 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
@@ -44,7 +44,7 @@
  * Creates and updates a veil that covers task contents on resize.
  */
 public class ResizeVeil {
-    private static final int RESIZE_ALPHA_DURATION = 200;
+    private static final int RESIZE_ALPHA_DURATION = 100;
     private final Context mContext;
     private final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
     private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
@@ -155,7 +155,6 @@
      * Animate veil's alpha to 0, fading it out.
      */
     public void hideVeil() {
-        final View resizeVeilView = mViewHost.getView();
         final ValueAnimator animator = new ValueAnimator();
         animator.setFloatValues(1, 0);
         animator.setDuration(RESIZE_ALPHA_DURATION);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index 56475a8..58c78e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -16,13 +16,22 @@
 
 package com.android.wm.shell.windowdecor;
 
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.transition.Transitions;
 
 import java.util.function.Supplier;
 
@@ -32,12 +41,14 @@
  * If the drag is resizing the task, we resize the veil instead.
  * If the drag is repositioning, we update in the typical manner.
  */
-public class VeiledResizeTaskPositioner implements DragPositioningCallback {
+public class VeiledResizeTaskPositioner implements DragPositioningCallback,
+        Transitions.TransitionHandler {
 
     private DesktopModeWindowDecoration mDesktopWindowDecoration;
     private ShellTaskOrganizer mTaskOrganizer;
     private DisplayController mDisplayController;
     private DragPositioningCallbackUtility.DragStartListener mDragStartListener;
+    private final Transitions mTransitions;
     private final Rect mStableBounds = new Rect();
     private final Rect mTaskBoundsAtDragStart = new Rect();
     private final PointF mRepositionStartPoint = new PointF();
@@ -46,28 +57,29 @@
     // finalize the bounds there using WCT#setBounds
     private final Rect mDisallowedAreaForEndBounds = new Rect();
     private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
-    private boolean mHasDragResized;
     private int mCtrlType;
 
     public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
             DesktopModeWindowDecoration windowDecoration, DisplayController displayController,
             Rect disallowedAreaForEndBounds,
-            DragPositioningCallbackUtility.DragStartListener dragStartListener) {
+            DragPositioningCallbackUtility.DragStartListener dragStartListener,
+            Transitions transitions) {
         this(taskOrganizer, windowDecoration, displayController, disallowedAreaForEndBounds,
-                dragStartListener, SurfaceControl.Transaction::new);
+                dragStartListener, SurfaceControl.Transaction::new, transitions);
     }
 
     public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
             DesktopModeWindowDecoration windowDecoration, DisplayController displayController,
             Rect disallowedAreaForEndBounds,
             DragPositioningCallbackUtility.DragStartListener dragStartListener,
-            Supplier<SurfaceControl.Transaction> supplier) {
+            Supplier<SurfaceControl.Transaction> supplier, Transitions transitions) {
         mTaskOrganizer = taskOrganizer;
         mDesktopWindowDecoration = windowDecoration;
         mDisplayController = displayController;
         mDragStartListener = dragStartListener;
         mDisallowedAreaForEndBounds.set(disallowedAreaForEndBounds);
         mTransactionSupplier = supplier;
+        mTransitions = transitions;
     }
 
     @Override
@@ -84,7 +96,6 @@
                 mTaskOrganizer.applyTransaction(wct);
             }
         }
-        mHasDragResized = false;
         mDragStartListener.onDragStart(mDesktopWindowDecoration.mTaskInfo.taskId);
         mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
     }
@@ -96,7 +107,6 @@
                 mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
                 mDisplayController, mDesktopWindowDecoration)) {
             mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
-            mHasDragResized = true;
         } else if (mCtrlType == CTRL_TYPE_UNDEFINED) {
             final SurfaceControl.Transaction t = mTransactionSupplier.get();
             DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration,
@@ -111,18 +121,23 @@
         PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y,
                 mRepositionStartPoint);
         if (isResizing()) {
-            if (mHasDragResized) {
+            if (!mTaskBoundsAtDragStart.equals(mRepositionTaskBounds)) {
                 DragPositioningCallbackUtility.changeBounds(
                         mCtrlType, mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds,
                         delta, mDisplayController, mDesktopWindowDecoration);
-                DragPositioningCallbackUtility.applyTaskBoundsChange(
-                        new WindowContainerTransaction(), mDesktopWindowDecoration,
-                        mRepositionTaskBounds, mTaskOrganizer);
+                mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
+                final WindowContainerTransaction wct = new WindowContainerTransaction();
+                wct.setBounds(mDesktopWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
+                if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+                    mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
+                } else {
+                    mTaskOrganizer.applyTransaction(wct);
+                }
+            } else {
+                // If bounds haven't changed, perform necessary veil reset here as startAnimation
+                // won't be called.
+                mDesktopWindowDecoration.hideResizeVeil();
             }
-            // TODO: (b/279062291) Synchronize the start of hide to the end of the draw triggered
-            //  above.
-            mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
-            mDesktopWindowDecoration.hideResizeVeil();
         } else if (!mDisallowedAreaForEndBounds.contains((int) x, (int) y)) {
             DragPositioningCallbackUtility.updateTaskBounds(mRepositionTaskBounds,
                     mTaskBoundsAtDragStart, mRepositionStartPoint, x, y);
@@ -133,7 +148,6 @@
         mCtrlType = CTRL_TYPE_UNDEFINED;
         mTaskBoundsAtDragStart.setEmpty();
         mRepositionStartPoint.set(0, 0);
-        mHasDragResized = false;
     }
 
     private boolean isResizing() {
@@ -141,4 +155,26 @@
                 || (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
     }
 
+    @Override
+    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        startTransaction.apply();
+        mDesktopWindowDecoration.hideResizeVeil();
+        mCtrlType = CTRL_TYPE_UNDEFINED;
+        finishCallback.onTransitionFinished(null, null);
+        return true;
+    }
+
+    /**
+     * We should never reach this as this handler's transitions are only started from shell
+     * explicitly.
+     */
+    @Nullable
+    @Override
+    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+            @NonNull TransitionRequestInfo request) {
+        return null;
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 9e988e8..7c1da35 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -211,7 +211,7 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(), any(),
+        verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
                 eq(SPLIT_POSITION_UNDEFINED), any());
     }
 
@@ -223,12 +223,12 @@
                 mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(), any(),
+        verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
                 eq(SPLIT_POSITION_TOP_OR_LEFT), any());
         reset(mSplitScreenStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(), any(),
+        verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
                 eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
@@ -240,12 +240,12 @@
                 mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(), any(),
+        verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
                 eq(SPLIT_POSITION_TOP_OR_LEFT), any());
         reset(mSplitScreenStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(), any(),
+        verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
                 eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index d0e2601..c37a497 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -181,7 +181,8 @@
         PendingIntent pendingIntent =
                 PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
 
-        mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+        mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
+                SPLIT_POSITION_TOP_OR_LEFT, null);
 
         verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
                 eq(SPLIT_POSITION_TOP_OR_LEFT), isNull());
@@ -200,7 +201,8 @@
                 createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
         doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
 
-        mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+        mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
+                SPLIT_POSITION_TOP_OR_LEFT, null);
 
         verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
                 eq(SPLIT_POSITION_TOP_OR_LEFT), isNull());
@@ -223,7 +225,8 @@
         ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
         doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any());
 
-        mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+        mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
+                SPLIT_POSITION_TOP_OR_LEFT, null);
 
         verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
                 isNull());
@@ -246,7 +249,8 @@
         doReturn(new ActivityManager.RecentTaskInfo()).when(mRecentTasks)
                 .findTaskInBackground(any());
 
-        mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+        mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
+                SPLIT_POSITION_TOP_OR_LEFT, null);
 
         verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
                 isNull());
@@ -265,7 +269,8 @@
         doReturn(sameTaskInfo).when(mSplitScreenController).getTaskInfo(
                 SPLIT_POSITION_BOTTOM_OR_RIGHT);
 
-        mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+        mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
+                SPLIT_POSITION_TOP_OR_LEFT, null);
 
         verify(mStageCoordinator).switchSplitPosition(anyString());
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index 9d56686..71ad0d7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -45,6 +45,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -58,6 +60,12 @@
     ActivityManager.RunningTaskInfo mTaskInfo;
     @Mock
     WindowContainerToken mToken;
+    @Mock
+    TaskViewTaskController mTaskViewTaskController2;
+    @Mock
+    ActivityManager.RunningTaskInfo mTaskInfo2;
+    @Mock
+    WindowContainerToken mToken2;
 
     TaskViewTransitions mTaskViewTransitions;
 
@@ -73,10 +81,16 @@
         mTaskInfo.token = mToken;
         mTaskInfo.taskId = 314;
         mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class);
+        when(mTaskViewTaskController.getTaskInfo()).thenReturn(mTaskInfo);
+
+        mTaskInfo2 = new ActivityManager.RunningTaskInfo();
+        mTaskInfo2.token = mToken2;
+        mTaskInfo2.taskId = 315;
+        mTaskInfo2.taskDescription = mock(ActivityManager.TaskDescription.class);
+        when(mTaskViewTaskController2.getTaskInfo()).thenReturn(mTaskInfo2);
 
         mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
         mTaskViewTransitions.addTaskView(mTaskViewTaskController);
-        when(mTaskViewTaskController.getTaskInfo()).thenReturn(mTaskInfo);
     }
 
     @Test
@@ -119,7 +133,7 @@
     }
 
     @Test
-    public void testSetTaskBounds_taskVisibleWithPending_noTransaction() {
+    public void testSetTaskBounds_taskVisibleWithPendingOpen_noTransaction() {
         assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
 
         mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);
@@ -135,6 +149,43 @@
     }
 
     @Test
+    public void testSetTaskBounds_taskVisibleWithPendingChange_transition() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+        mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);
+
+        // Consume the pending transition from visibility change
+        TaskViewTransitions.PendingTransition pending =
+                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
+        assertThat(pending).isNotNull();
+        mTaskViewTransitions.startAnimation(pending.mClaimed,
+                mock(TransitionInfo.class),
+                new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(),
+                mock(Transitions.TransitionFinishCallback.class));
+        // Verify it was consumed
+        TaskViewTransitions.PendingTransition checkPending =
+                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
+        assertThat(checkPending).isNull();
+
+        // Test that set bounds creates a new transition
+        mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
+                new Rect(0, 0, 100, 100));
+        assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE))
+                .isNotNull();
+
+        // Test that set bounds again (with different bounds) creates another transition
+        mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
+                new Rect(0, 0, 300, 200));
+        List<TaskViewTransitions.PendingTransition> pendingList =
+                mTaskViewTransitions.findAllPending(mTaskViewTaskController)
+                        .stream()
+                        .filter(pendingTransition -> pendingTransition.mType == TRANSIT_CHANGE)
+                        .toList();
+        assertThat(pendingList.size()).isEqualTo(2);
+    }
+
+    @Test
     public void testSetTaskBounds_sameBounds_noTransaction() {
         assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
 
@@ -161,6 +212,16 @@
                 mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE);
         assertThat(pendingBounds).isNotNull();
 
+        // Test that setting same bounds with in-flight transition doesn't cause another one
+        mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
+                new Rect(0, 0, 100, 100));
+        List<TaskViewTransitions.PendingTransition> pendingList =
+                mTaskViewTransitions.findAllPending(mTaskViewTaskController)
+                        .stream()
+                        .filter(pendingTransition -> pendingTransition.mType == TRANSIT_CHANGE)
+                        .toList();
+        assertThat(pendingList.size()).isEqualTo(1);
+
         // Consume the pending bounds transaction
         mTaskViewTransitions.startAnimation(pendingBounds.mClaimed,
                 mock(TransitionInfo.class),
@@ -180,6 +241,42 @@
         assertThat(pendingBounds2).isNull();
     }
 
+
+    @Test
+    public void testSetTaskBounds_taskVisibleWithDifferentTaskViewPendingChange_transition() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+        mTaskViewTransitions.addTaskView(mTaskViewTaskController2);
+
+        mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);
+
+        // Consume the pending transition from visibility change
+        TaskViewTransitions.PendingTransition pending =
+                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
+        assertThat(pending).isNotNull();
+        mTaskViewTransitions.startAnimation(pending.mClaimed,
+                mock(TransitionInfo.class),
+                new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(),
+                mock(Transitions.TransitionFinishCallback.class));
+        // Verify it was consumed
+        TaskViewTransitions.PendingTransition checkPending =
+                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
+        assertThat(checkPending).isNull();
+
+        // Set the second taskview as visible & check that it has a pending transition
+        mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController2, true);
+        TaskViewTransitions.PendingTransition pending2 =
+                mTaskViewTransitions.findPending(mTaskViewTaskController2, TRANSIT_TO_FRONT);
+        assertThat(pending2).isNotNull();
+
+        // Test that set bounds on the first taskview will create a new transition
+        mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
+                new Rect(0, 0, 100, 100));
+        assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE))
+                .isNotNull();
+    }
+
     @Test
     public void testSetTaskVisibility_taskRemoved_noNPE() {
         mTaskViewTransitions.removeTaskView(mTaskViewTaskController);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
index 4c27706..41bab95 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
@@ -54,6 +54,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeController;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -87,6 +88,7 @@
     @Mock private DesktopTasksController mDesktopTasksController;
     @Mock private InputMonitor mInputMonitor;
     @Mock private InputManager mInputManager;
+    @Mock private Transitions mTransitions;
     @Mock private DesktopModeWindowDecorViewModel.InputMonitorFactory mMockInputMonitorFactory;
     @Mock private Supplier<SurfaceControl.Transaction> mTransactionFactory;
     @Mock private SurfaceControl.Transaction mTransaction;
@@ -106,6 +108,7 @@
                 mTaskOrganizer,
                 mDisplayController,
                 mSyncQueue,
+                mTransitions,
                 Optional.of(mDesktopModeController),
                 Optional.of(mDesktopTasksController),
                 Optional.of(mSplitScreenController),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 337e40d..4147dd8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -22,12 +22,14 @@
 import android.testing.AndroidTestingRunner
 import android.view.Display
 import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CHANGE
 import android.window.WindowContainerToken
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED
@@ -78,6 +80,8 @@
     private lateinit var mockTransactionFactory: Supplier<SurfaceControl.Transaction>
     @Mock
     private lateinit var mockTransaction: SurfaceControl.Transaction
+    @Mock
+    private lateinit var mockTransitions: Transitions
 
     private lateinit var taskPositioner: VeiledResizeTaskPositioner
 
@@ -92,7 +96,8 @@
                 mockDisplayController,
                 DISALLOWED_AREA_FOR_END_BOUNDS,
                 mockDragStartListener,
-                mockTransactionFactory
+                mockTransactionFactory,
+                mockTransitions
             )
 
         whenever(taskToken.asBinder()).thenReturn(taskBinder)
@@ -129,6 +134,12 @@
             STARTING_BOUNDS.left.toFloat(),
             STARTING_BOUNDS.top.toFloat()
         )
+        verify(mockTransitions, never()).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+                        change.configuration.windowConfiguration.bounds == STARTING_BOUNDS}},
+            eq(taskPositioner))
         verify(mockDesktopWindowDecoration).hideResizeVeil()
     }
 
@@ -206,15 +217,12 @@
         rectAfterEnd.right += 10
         rectAfterEnd.top += 10
         verify(mockDesktopWindowDecoration, times(2)).updateResizeVeil(any())
-        verify(mockDesktopWindowDecoration).hideResizeVeil()
-
-        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+        verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
                 token == taskBinder &&
                         (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
-                        change.configuration.windowConfiguration.bounds == rectAfterEnd
-            }
-        })
+                        change.configuration.windowConfiguration.bounds == rectAfterEnd}},
+            eq(taskPositioner))
     }
 
     @Test
@@ -235,7 +243,13 @@
             STARTING_BOUNDS.left.toFloat() + 10,
             STARTING_BOUNDS.top.toFloat() + 10
         )
-        verify(mockDesktopWindowDecoration).hideResizeVeil()
+
+        verify(mockTransitions, never()).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+                        change.configuration.windowConfiguration.bounds == STARTING_BOUNDS}},
+            eq(taskPositioner))
 
         verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 7e238e4..0e9c162 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -57,6 +57,30 @@
         }
     };
 
+    /**
+     * The {@link #getOriginalId() original id} of the route that represents the built-in media
+     * route.
+     *
+     * <p>A route with this id will only be visible to apps with permission to do system routing,
+     * which means having {@link android.Manifest.permission#BLUETOOTH_CONNECT} and {@link
+     * android.Manifest.permission#BLUETOOTH_SCAN}, or {@link
+     * android.Manifest.permission#MODIFY_AUDIO_ROUTING}.
+     *
+     * @hide
+     */
+    public static final String ROUTE_ID_DEVICE = "DEVICE_ROUTE";
+
+    /**
+     * The {@link #getOriginalId() original id} of the route that represents the default system
+     * media route.
+     *
+     * <p>A route with this id will be visible to apps with no permission over system routing. See
+     * {@link #ROUTE_ID_DEVICE} for details.
+     *
+     * @hide
+     */
+    public static final String ROUTE_ID_DEFAULT = "DEFAULT_ROUTE";
+
     /** @hide */
     @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
             CONNECTION_STATE_CONNECTED})
diff --git a/packages/SettingsLib/MainSwitchPreference/res/color-night-v34/settingslib_main_switch_text_color.xml b/packages/SettingsLib/MainSwitchPreference/res/color-night-v34/settingslib_main_switch_text_color.xml
new file mode 100644
index 0000000..a7ccfc2
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/color-night-v34/settingslib_main_switch_text_color.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:color="@android:color/system_accent1_100"
+          android:alpha="?android:attr/disabledAlpha" />
+    <item android:color="@android:color/system_accent1_100"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/res/color-v31/settingslib_main_switch_text_color.xml b/packages/SettingsLib/MainSwitchPreference/res/color-v31/settingslib_main_switch_text_color.xml
new file mode 100644
index 0000000..de367244
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/color-v31/settingslib_main_switch_text_color.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:color="@android:color/black"
+          android:alpha="?android:attr/disabledAlpha" />
+    <item android:color="@android:color/black"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/res/color-v34/settingslib_main_switch_text_color.xml b/packages/SettingsLib/MainSwitchPreference/res/color-v34/settingslib_main_switch_text_color.xml
new file mode 100644
index 0000000..e33905b
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/color-v34/settingslib_main_switch_text_color.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:color="@android:color/system_accent1_900"
+          android:alpha="?android:attr/disabledAlpha" />
+    <item android:color="@android:color/system_accent1_900"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml
index 1b80cc6..b8e43fc2 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml
@@ -20,6 +20,6 @@
     <style name="MainSwitchText.Settingslib" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse">
         <item name="android:textSize">20sp</item>
         <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
-        <item name="android:textColor">@android:color/black</item>
+        <item name="android:textColor">@color/settingslib_main_switch_text_color</item>
     </style>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/color-night-v34/settingslib_switch_track_outline_color.xml b/packages/SettingsLib/SettingsTheme/res/color-night-v34/settingslib_switch_track_outline_color.xml
new file mode 100644
index 0000000..6fab831
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-night-v34/settingslib_switch_track_outline_color.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Disabled status of thumb -->
+    <item android:state_enabled="false"
+          android:color="@android:color/system_neutral2_400"
+          android:alpha="?android:attr/disabledAlpha" />
+    <!-- Toggle off status of thumb -->
+    <item android:state_checked="false"
+          android:color="@android:color/system_neutral2_400" />
+    <!-- Enabled or toggle on status of thumb -->
+    <item android:color="@color/settingslib_track_on_color" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_switch_thumb_color.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_switch_thumb_color.xml
index 8ccbb06..a9c5c03 100644
--- a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_switch_thumb_color.xml
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_switch_thumb_color.xml
@@ -17,7 +17,8 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <!-- Disabled status of thumb -->
     <item android:state_enabled="false"
-          android:color="@color/settingslib_thumb_disabled_color" />
+          android:color="@color/settingslib_thumb_disabled_color"
+          android:alpha="?android:attr/disabledAlpha" />
     <!-- Toggle off status of thumb -->
     <item android:state_checked="false"
           android:color="@color/settingslib_thumb_off_color" />
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v34/settingslib_switch_thumb_color.xml b/packages/SettingsLib/SettingsTheme/res/color-v34/settingslib_switch_thumb_color.xml
new file mode 100644
index 0000000..2bc4ddf
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v34/settingslib_switch_thumb_color.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Disabled status of thumb -->
+    <item android:state_enabled="false"
+          android:color="@color/settingslib_thumb_disabled_color"
+          android:alpha="?android:attr/disabledAlpha" />
+    <!-- Toggle off status of thumb -->
+    <item android:state_checked="false"
+          android:color="@color/settingslib_thumb_off_color" />
+    <!-- Enabled or toggle on status of thumb -->
+    <item android:color="@color/settingslib_thumb_on_color" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v34/settingslib_switch_track_outline_color.xml b/packages/SettingsLib/SettingsTheme/res/color-v34/settingslib_switch_track_outline_color.xml
new file mode 100644
index 0000000..8abc1fd
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v34/settingslib_switch_track_outline_color.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Disabled status of thumb -->
+    <item android:state_enabled="false"
+          android:color="@android:color/system_neutral2_500"
+          android:alpha="?android:attr/disabledAlpha" />
+    <!-- Toggle off status of thumb -->
+    <item android:state_checked="false"
+          android:color="@android:color/system_neutral2_500" />
+    <!-- Enabled or toggle on status of thumb -->
+    <item android:color="@color/settingslib_track_on_color" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v34/settingslib_switch_track.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v34/settingslib_switch_track.xml
new file mode 100644
index 0000000..851e413
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v34/settingslib_switch_track.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle"
+    android:width="52dp"
+    android:height="28dp">
+
+    <solid android:color="@color/settingslib_switch_track_color" />
+    <corners android:radius="35dp" />
+    <stroke android:width="2dp" android:color="@color/settingslib_track_online_color"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v34/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v34/colors.xml
new file mode 100644
index 0000000..e3645b5
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v34/colors.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<resources>
+    <!-- Material next state on color-->
+    <color name="settingslib_state_on_color">@android:color/system_accent1_700</color>
+
+    <!-- Material next state off color-->
+    <color name="settingslib_state_off_color">@android:color/system_accent1_700</color>
+
+    <!-- Material next thumb disable color-->
+    <color name="settingslib_thumb_disabled_color">@color/settingslib_thumb_off_color</color>
+
+    <!-- Material next thumb off color-->
+    <color name="settingslib_thumb_on_color">@android:color/system_accent1_800</color>
+
+    <!-- Material next thumb off color-->
+    <color name="settingslib_thumb_off_color">@android:color/system_neutral2_400</color>
+
+    <!-- Material next track on color-->
+    <color name="settingslib_track_on_color">@android:color/system_accent1_200</color>
+
+    <!-- Material next track off color-->
+    <color name="settingslib_track_off_color">@android:color/system_surface_container_highest_dark
+    </color>
+</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v34/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v34/colors.xml
new file mode 100644
index 0000000..fdd96ec
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v34/colors.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<resources>
+    <!-- Material next state on color-->
+    <color name="settingslib_state_on_color">@android:color/system_accent1_100</color>
+
+    <!-- Material next state off color-->
+    <color name="settingslib_state_off_color">@android:color/system_accent1_100</color>
+
+    <!-- Material next thumb disable color-->
+    <color name="settingslib_thumb_disabled_color">@color/settingslib_thumb_off_color</color>
+
+    <!-- Material next thumb off color-->
+    <color name="settingslib_thumb_on_color">@android:color/system_accent1_0</color>
+
+    <!-- Material next thumb off color-->
+    <color name="settingslib_thumb_off_color">@android:color/system_neutral2_500</color>
+
+    <!-- Material next track on color-->
+    <color name="settingslib_track_on_color">@android:color/system_accent1_600</color>
+
+    <!-- Material next track off color-->
+    <color name="settingslib_track_off_color">@android:color/system_surface_container_highest_light</color>
+
+    <!-- Material next track outline color-->
+    <color name="settingslib_track_online_color">@color/settingslib_switch_track_outline_color</color>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml b/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml
new file mode 100644
index 0000000..f7dac13
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <com.android.keyguard.BouncerKeyguardMessageArea
+        android:id="@+id/bouncer_primary_message_area"
+        style="@style/Keyguard.Bouncer.PrimaryMessage"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/keyguard_lock_padding"
+        android:focusable="true"
+         />
+
+    <com.android.keyguard.BouncerKeyguardMessageArea
+        android:id="@+id/bouncer_secondary_message_area"
+        style="@style/Keyguard.Bouncer.SecondaryMessage"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/secondary_message_padding"
+        android:focusable="true" />
+
+</merge>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 4b94707..3fc0965 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -25,7 +25,7 @@
     android:layout_height="wrap_content"
     android:clipChildren="false"
     android:layout_gravity="center_horizontal|top">
-    <FrameLayout
+    <com.android.keyguard.KeyguardClockFrame
         android:id="@+id/lockscreen_clock_view"
         android:layout_width="wrap_content"
         android:layout_height="@dimen/small_clock_height"
@@ -34,7 +34,7 @@
         android:clipChildren="false"
         android:paddingStart="@dimen/clock_padding_start"
         android:visibility="invisible" />
-    <FrameLayout
+    <com.android.keyguard.KeyguardClockFrame
         android:id="@+id/lockscreen_clock_view_large"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index 6ec65ce..3412a30 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -29,6 +29,13 @@
     >
     <include layout="@layout/keyguard_bouncer_message_area"/>
 
+    <com.android.systemui.keyguard.bouncer.ui.BouncerMessageView
+        android:id="@+id/bouncer_message_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+    />
+
     <Space
         android:layout_width="match_parent"
         android:layout_height="0dp"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index c772c96..78a7c17 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -33,6 +33,12 @@
     android:clipToPadding="false">
     <include layout="@layout/keyguard_bouncer_message_area"/>
 
+    <com.android.systemui.keyguard.bouncer.ui.BouncerMessageView
+        android:id="@+id/bouncer_message_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" />
+
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/pattern_container"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index f61df05..330676b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -29,6 +29,12 @@
     androidprv:layout_maxWidth="@dimen/keyguard_security_width">
 <include layout="@layout/keyguard_bouncer_message_area"/>
 
+<com.android.systemui.keyguard.bouncer.ui.BouncerMessageView
+    android:id="@+id/bouncer_message_view"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" />
+
 <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/pin_container"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 647abee..557fbf2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -30,7 +30,7 @@
     android:clipChildren="false"
     android:layout_width="0dp"
     android:layout_height="wrap_content">
-    <LinearLayout
+    <com.android.keyguard.KeyguardStatusContainer
         android:id="@+id/status_view_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -48,5 +48,5 @@
             android:layout_height="wrap_content"
             android:padding="@dimen/qs_media_padding"
             />
-    </LinearLayout>
+    </com.android.keyguard.KeyguardStatusContainer>
 </com.android.keyguard.KeyguardStatusView>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 4d289eb..88f7bcd 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -23,6 +23,26 @@
     <style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
         <item name="android:textSize">@dimen/kg_status_line_font_size</item>
         <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+    </style>
+    <style name="Keyguard.Bouncer.PrimaryMessage" parent="Theme.SystemUI">
+        <item name="android:textSize">18sp</item>
+        <item name="android:lineHeight">24dp</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:textAlignment">center</item>
+        <item name="android:ellipsize">marquee</item>
+    </style>
+    <style name="Keyguard.Bouncer.SecondaryMessage" parent="Theme.SystemUI">
+        <item name="android:textSize">14sp</item>
+        <item name="android:lineHeight">20dp</item>
+        <item name="android:maxLines">@integer/bouncer_secondary_message_lines</item>
+        <item name="android:lines">@integer/bouncer_secondary_message_lines</item>
+        <item name="android:textAlignment">center</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+        <item name="android:ellipsize">end</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
     </style>
     <style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI">
         <item name="android:textColor">?androidprv:attr/materialColorOnTertiaryFixed</item>
diff --git a/packages/SystemUI/res/raw/sfps_pulse.json b/packages/SystemUI/res/raw/sfps_pulse.json
index c4903a2..9fe4eb6 100644
--- a/packages/SystemUI/res/raw/sfps_pulse.json
+++ b/packages/SystemUI/res/raw/sfps_pulse.json
@@ -1 +1 @@
-{"v":"5.7.6","fr":60,"ip":0,"op":300,"w":42,"h":80,"nm":"Fingerprint Pulse Motion","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[28,40,0],"to":[0.751,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[32.503,40,0],"to":[0,0,0],"ti":[0.751,0,0]},{"t":300,"s":[28,40,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[41.878,40,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[20]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[20]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[20]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[20]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[20]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[20]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[20]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[20]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[20]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[20]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[20]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]}
\ No newline at end of file
+{"v":"5.7.13","fr":60,"ip":0,"op":300,"w":42,"h":80,"nm":"sfps_pulse_motion_04","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.617,"y":0.539},"o":{"x":0.477,"y":0},"t":0,"s":[28,40,0],"to":[0.365,0,0],"ti":[-1.009,0,0]},{"i":{"x":0.436,"y":1},"o":{"x":0.17,"y":0.547},"t":10,"s":[30.576,40,0],"to":[1.064,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[32.503,40,0],"to":[0,0,0],"ti":[0.751,0,0]},{"t":300,"s":[28,40,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[41.878,40,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[75]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[75]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[75]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[75]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[75]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[75]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[75]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[75]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[75]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[75]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[75]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/sfps_pulse_landscape.json b/packages/SystemUI/res/raw/sfps_pulse_landscape.json
index 8c91762..42b92eb 100644
--- a/packages/SystemUI/res/raw/sfps_pulse_landscape.json
+++ b/packages/SystemUI/res/raw/sfps_pulse_landscape.json
@@ -1 +1 @@
-{"v":"5.7.6","fr":60,"ip":0,"op":300,"w":80,"h":42,"nm":"Fingerprint Pulse Motion Portrait","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[40,14,0],"to":[0,-0.751,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,-0.751,0]},{"t":300,"s":[40,14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,0.122,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[20]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[20]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[20]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[20]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[20]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[20]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[20]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[20]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[20]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[20]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[20]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]}
\ No newline at end of file
+{"v":"5.7.13","fr":60,"ip":0,"op":300,"w":80,"h":42,"nm":"sfps_pulse_motion_portrait","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[40,14,0],"to":[0,-0.751,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,-0.751,0]},{"t":300,"s":[40,14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,0.122,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[75]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[75]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[75]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[75]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[75]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[75]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[75]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[75]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[75]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[75]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[75]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 26d6875..b4e1b66 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -577,6 +577,9 @@
     <!-- Whether to show the side fps hint while on bouncer -->
     <bool name="config_show_sidefps_hint_on_bouncer">true</bool>
 
+    <!-- Max number of lines we want to show for the bouncer secondary message -->
+    <integer name="bouncer_secondary_message_lines">2</integer>
+
     <!-- Whether to use the split 2-column notification shade -->
     <bool name="config_use_split_notification_shade">false</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 64615fb..7187de8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -807,6 +807,7 @@
     <!-- The width/height of the unlock icon view on keyguard. -->
     <dimen name="keyguard_lock_height">42dp</dimen>
     <dimen name="keyguard_lock_padding">20dp</dimen>
+    <dimen name="secondary_message_padding">8dp</dimen>
 
     <dimen name="keyguard_security_container_padding_top">20dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
index a82f0e3..0842953 100644
--- a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
+++ b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
@@ -22,8 +22,6 @@
 import android.animation.ObjectAnimator
 import android.content.Context
 import android.content.res.ColorStateList
-import android.content.res.TypedArray
-import android.graphics.Color
 import android.util.AttributeSet
 import android.view.View
 import com.android.app.animation.Interpolators
@@ -41,12 +39,28 @@
     protected open val SHOW_DURATION_MILLIS = 150L
     protected open val HIDE_DURATION_MILLIS = 200L
 
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        mDefaultColorState = getColorInStyle()
+    }
+
+    private fun getColorInStyle(): ColorStateList? {
+        val styledAttributes =
+            context.obtainStyledAttributes(styleResId, intArrayOf(android.R.attr.textColor))
+        var colorStateList: ColorStateList? = null
+        if (styledAttributes != null) {
+            colorStateList = styledAttributes.getColorStateList(0)
+        }
+        styledAttributes.recycle()
+        return colorStateList
+    }
+
     override fun updateTextColor() {
         var colorState = mDefaultColorState
         mNextMessageColorState?.defaultColor?.let { color ->
             if (color != DEFAULT_COLOR) {
                 colorState = mNextMessageColorState
-                mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR)
+                mNextMessageColorState = mDefaultColorState ?: ColorStateList.valueOf(DEFAULT_COLOR)
             }
         }
         setTextColor(colorState)
@@ -57,15 +71,12 @@
     }
 
     override fun onThemeChanged() {
-        val array: TypedArray = mContext.obtainStyledAttributes(intArrayOf(TITLE))
-        val newTextColors: ColorStateList = ColorStateList.valueOf(array.getColor(0, Color.RED))
-        array.recycle()
-        mDefaultColorState = newTextColors
+        mDefaultColorState = getColorInStyle() ?: Utils.getColorAttr(context, TITLE)
         super.onThemeChanged()
     }
 
     override fun reloadColor() {
-        mDefaultColorState = Utils.getColorAttr(context, TITLE)
+        mDefaultColorState = getColorInStyle() ?: Utils.getColorAttr(context, TITLE)
         super.reloadColor()
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
index b153785..1f75e81 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
@@ -16,6 +16,11 @@
 
 package com.android.keyguard;
 
+import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ACTIVE_DATA_SUB_CHANGED;
+import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ON_SIM_STATE_CHANGED;
+import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ON_TELEPHONY_CAPABLE;
+import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_REFRESH_CARRIER_INFO;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -32,6 +37,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.keyguard.logging.CarrierTextManagerLogger;
 import com.android.settingslib.WirelessUtils;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -40,6 +46,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository;
 import com.android.systemui.telephony.TelephonyListenerManager;
 
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
@@ -68,6 +75,7 @@
     private final AtomicBoolean mNetworkSupported = new AtomicBoolean();
     @VisibleForTesting
     protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final CarrierTextManagerLogger mLogger;
     private final WifiRepository mWifiRepository;
     private final boolean[] mSimErrorState;
     private final int mSimSlotsNumber;
@@ -97,19 +105,13 @@
     protected final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
         @Override
         public void onRefreshCarrierInfo() {
-            if (DEBUG) {
-                Log.d(TAG, "onRefreshCarrierInfo(), mTelephonyCapable: "
-                        + Boolean.toString(mTelephonyCapable));
-            }
+            mLogger.logUpdateCarrierTextForReason(REASON_REFRESH_CARRIER_INFO);
             updateCarrierText();
         }
 
         @Override
         public void onTelephonyCapable(boolean capable) {
-            if (DEBUG) {
-                Log.d(TAG, "onTelephonyCapable() mTelephonyCapable: "
-                        + Boolean.toString(capable));
-            }
+            mLogger.logUpdateCarrierTextForReason(REASON_ON_TELEPHONY_CAPABLE);
             mTelephonyCapable = capable;
             updateCarrierText();
         }
@@ -121,7 +123,7 @@
                 return;
             }
 
-            if (DEBUG) Log.d(TAG, "onSimStateChanged: " + getStatusForIccState(simState));
+            mLogger.logUpdateCarrierTextForReason(REASON_ON_SIM_STATE_CHANGED);
             if (getStatusForIccState(simState) == CarrierTextManager.StatusMode.SimIoError) {
                 mSimErrorState[slotId] = true;
                 updateCarrierText();
@@ -137,6 +139,7 @@
         @Override
         public void onActiveDataSubscriptionIdChanged(int subId) {
             if (mNetworkSupported.get() && mCarrierTextCallback != null) {
+                mLogger.logUpdateCarrierTextForReason(REASON_ACTIVE_DATA_SUB_CHANGED);
                 updateCarrierText();
             }
         }
@@ -175,7 +178,9 @@
             WakefulnessLifecycle wakefulnessLifecycle,
             @Main Executor mainExecutor,
             @Background Executor bgExecutor,
-            KeyguardUpdateMonitor keyguardUpdateMonitor) {
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            CarrierTextManagerLogger logger) {
+
         mContext = context;
         mIsEmergencyCallCapable = telephonyManager.isVoiceCapable();
 
@@ -191,6 +196,7 @@
         mMainExecutor = mainExecutor;
         mBgExecutor = bgExecutor;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mLogger = logger;
         mBgExecutor.execute(() -> {
             boolean supported = mContext.getPackageManager()
                     .hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
@@ -315,7 +321,7 @@
             subOrderBySlot[i] = -1;
         }
         final CharSequence[] carrierNames = new CharSequence[numSubs];
-        if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs);
+        mLogger.logUpdate(numSubs);
 
         for (int i = 0; i < numSubs; i++) {
             int subId = subs.get(i).getSubscriptionId();
@@ -325,9 +331,7 @@
             int simState = mKeyguardUpdateMonitor.getSimState(subId);
             CharSequence carrierName = subs.get(i).getCarrierName();
             CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
-            if (DEBUG) {
-                Log.d(TAG, "Handling (subId=" + subId + "): " + simState + " " + carrierName);
-            }
+            mLogger.logUpdateLoopStart(subId, simState, String.valueOf(carrierName));
             if (carrierTextForSimState != null) {
                 allSimsMissing = false;
                 carrierNames[i] = carrierTextForSimState;
@@ -340,9 +344,7 @@
                     // Wi-Fi is disassociated or disabled
                     if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
                             || mWifiRepository.isWifiConnectedWithValidSsid()) {
-                        if (DEBUG) {
-                            Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + ss);
-                        }
+                        mLogger.logUpdateWfcCheck();
                         anySimReadyAndInService = true;
                     }
                 }
@@ -379,7 +381,7 @@
                     if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)) {
                         plmn = i.getStringExtra(TelephonyManager.EXTRA_PLMN);
                     }
-                    if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
+                    mLogger.logUpdateFromStickyBroadcast(plmn, spn);
                     if (Objects.equals(plmn, spn)) {
                         text = plmn;
                     } else {
@@ -409,6 +411,7 @@
                 !allSimsMissing,
                 subsIds,
                 airplaneMode);
+        mLogger.logCallbackSentFromUpdate(info);
         postToCallback(info);
         Trace.endSection();
     }
@@ -645,6 +648,7 @@
         private final Executor mMainExecutor;
         private final Executor mBgExecutor;
         private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+        private final CarrierTextManagerLogger mLogger;
         private boolean mShowAirplaneMode;
         private boolean mShowMissingSim;
 
@@ -658,7 +662,8 @@
                 WakefulnessLifecycle wakefulnessLifecycle,
                 @Main Executor mainExecutor,
                 @Background Executor bgExecutor,
-                KeyguardUpdateMonitor keyguardUpdateMonitor) {
+                KeyguardUpdateMonitor keyguardUpdateMonitor,
+                CarrierTextManagerLogger logger) {
             mContext = context;
             mSeparator = resources.getString(
                     com.android.internal.R.string.kg_text_message_separator);
@@ -669,6 +674,7 @@
             mMainExecutor = mainExecutor;
             mBgExecutor = bgExecutor;
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+            mLogger = logger;
         }
 
         /** */
@@ -688,7 +694,7 @@
             return new CarrierTextManager(
                     mContext, mSeparator, mShowAirplaneMode, mShowMissingSim, mWifiRepository,
                     mTelephonyManager, mTelephonyListenerManager, mWakefulnessLifecycle,
-                    mMainExecutor, mBgExecutor, mKeyguardUpdateMonitor);
+                    mMainExecutor, mBgExecutor, mKeyguardUpdateMonitor, mLogger);
         }
     }
     /**
@@ -716,6 +722,17 @@
             this.subscriptionIds = subscriptionIds;
             this.airplaneMode = airplaneMode;
         }
+
+        @Override
+        public String toString() {
+            return "CarrierTextCallbackInfo{"
+                    + "carrierText=" + carrierText
+                    + ", listOfCarriers=" + Arrays.toString(listOfCarriers)
+                    + ", anySimReady=" + anySimReady
+                    + ", subscriptionIds=" + Arrays.toString(subscriptionIds)
+                    + ", airplaneMode=" + airplaneMode
+                    + '}';
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index b18abf3..c6d1471 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -22,6 +22,8 @@
 import android.view.KeyEvent;
 import android.view.View;
 
+import androidx.annotation.CallSuper;
+
 import com.android.internal.widget.LockscreenCredential;
 import com.android.systemui.R;
 
@@ -48,7 +50,9 @@
     protected abstract void resetState();
 
     @Override
+    @CallSuper
     protected void onFinishInflate() {
+        super.onFinishInflate();
         mEcaView = findViewById(R.id.keyguard_selector_fade_container);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index a229b13..e057188 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -37,6 +37,7 @@
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingClassifier;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.flags.FeatureFlags;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -77,9 +78,10 @@
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
             LatencyTracker latencyTracker, FalsingCollector falsingCollector,
-            EmergencyButtonController emergencyButtonController) {
+            EmergencyButtonController emergencyButtonController,
+            FeatureFlags featureFlags) {
         super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
-                messageAreaControllerFactory);
+                messageAreaControllerFactory, featureFlags);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockPatternUtils = lockPatternUtils;
         mLatencyTracker = latencyTracker;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt
new file mode 100644
index 0000000..d821d30
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt
@@ -0,0 +1,39 @@
+package com.android.keyguard
+
+import android.content.Context
+import android.graphics.Canvas
+import android.util.AttributeSet
+import android.view.View
+import android.widget.FrameLayout
+
+class KeyguardClockFrame(
+    context: Context,
+    attrs: AttributeSet,
+) : FrameLayout(context, attrs) {
+    private var drawAlpha: Int = 255
+
+    protected override fun onSetAlpha(alpha: Int): Boolean {
+        drawAlpha = alpha
+        return true
+    }
+
+    protected override fun dispatchDraw(canvas: Canvas) {
+        val restoreTo = saveCanvasAlpha(this, canvas, drawAlpha)
+        super.dispatchDraw(canvas)
+        canvas.restoreToCount(restoreTo)
+    }
+
+    companion object {
+        @JvmStatic
+        fun saveCanvasAlpha(view: View, canvas: Canvas, alpha: Int): Int {
+            var (x, y) =
+                run {
+                    val locationOnScreen = IntArray(2)
+                    view.getLocationOnScreen(locationOnScreen)
+                    Pair(locationOnScreen[0].toFloat(), locationOnScreen[1].toFloat())
+                }
+
+            return canvas.saveLayerAlpha(-1f * x, -1f * y, x + view.width, y + view.height, alpha)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 644a9bc..05a8f9f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -5,11 +5,11 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.FrameLayout;
 import android.widget.RelativeLayout;
 
 import androidx.annotation.IntDef;
@@ -69,12 +69,13 @@
     /**
      * Frame for small/large clocks
      */
-    private FrameLayout mSmallClockFrame;
-    private FrameLayout mLargeClockFrame;
+    private KeyguardClockFrame mSmallClockFrame;
+    private KeyguardClockFrame mLargeClockFrame;
     private ClockController mClock;
 
     private View mStatusArea;
     private int mSmartspaceTopOffset;
+    private int mDrawAlpha = 255;
 
     /**
      * Maintain state so that a newly connected plugin can be initialized.
@@ -121,6 +122,19 @@
         onDensityOrFontScaleChanged();
     }
 
+    @Override
+    protected boolean onSetAlpha(int alpha) {
+        mDrawAlpha = alpha;
+        return true;
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        int restoreTo = KeyguardClockFrame.saveCanvasAlpha(this, canvas, mDrawAlpha);
+        super.dispatchDraw(canvas);
+        canvas.restoreToCount(restoreTo);
+    }
+
     public void setLogBuffer(LogBuffer logBuffer) {
         mLogBuffer = logBuffer;
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 676f342..d8bf570 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -179,20 +179,6 @@
     }
 
     /**
-     * Set alpha directly to mView will clip clock, so we set alpha to clock face instead
-     */
-    public void setAlpha(float alpha) {
-        ClockController clock = getClock();
-        if (clock != null) {
-            clock.getLargeClock().getView().setAlpha(alpha);
-            clock.getSmallClock().getView().setAlpha(alpha);
-        }
-        if (mStatusArea != null) {
-            mStatusArea.setAlpha(alpha);
-        }
-    }
-
-    /**
      * Attach the controller to the view it relates to.
      */
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index 7eae729..c9d9069 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -21,11 +21,14 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
+import android.view.View;
 import android.widget.LinearLayout;
 
+import androidx.annotation.CallSuper;
 import androidx.annotation.Nullable;
 
 import com.android.internal.jank.InteractionJankMonitor;
+import com.android.systemui.R;
 
 /**
  * A Base class for all Keyguard password/pattern/pin related inputs.
@@ -33,6 +36,9 @@
 public abstract class KeyguardInputView extends LinearLayout {
     private Runnable mOnFinishImeAnimationRunnable;
 
+    @Nullable
+    private View mBouncerMessageView;
+
     public KeyguardInputView(Context context) {
         super(context);
     }
@@ -87,6 +93,18 @@
         mOnFinishImeAnimationRunnable = onFinishImeAnimationRunnable;
     }
 
+    @Override
+    @CallSuper
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mBouncerMessageView = findViewById(R.id.bouncer_message_view);
+    }
+
+    @Nullable
+    public final View getBouncerMessageView() {
+        return mBouncerMessageView;
+    }
+
     public void runOnFinishImeAnimationRunnable() {
         if (mOnFinishImeAnimationRunnable != null) {
             mOnFinishImeAnimationRunnable.run();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index a0f5f34..3c05299 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -16,6 +16,7 @@
 
 package com.android.keyguard;
 
+import android.annotation.CallSuper;
 import android.annotation.Nullable;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -31,6 +32,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -53,16 +55,19 @@
     // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
     // state for the current security method.
     private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {};
+    private final FeatureFlags mFeatureFlags;
 
     protected KeyguardInputViewController(T view, SecurityMode securityMode,
             KeyguardSecurityCallback keyguardSecurityCallback,
             EmergencyButtonController emergencyButtonController,
-            @Nullable KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
+            @Nullable KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+            FeatureFlags featureFlags) {
         super(view);
         mSecurityMode = securityMode;
         mKeyguardSecurityCallback = keyguardSecurityCallback;
         mEmergencyButton = view == null ? null : view.findViewById(R.id.emergency_call_button);
         mEmergencyButtonController = emergencyButtonController;
+        mFeatureFlags = featureFlags;
         if (messageAreaControllerFactory != null) {
             try {
                 BouncerKeyguardMessageArea kma = view.requireViewById(R.id.bouncer_message_area);
@@ -82,9 +87,21 @@
     }
 
     @Override
+    @CallSuper
     protected void onViewAttached() {
+        updateMessageAreaVisibility();
     }
 
+    private void updateMessageAreaVisibility() {
+        if (mMessageAreaController == null) return;
+        if (mFeatureFlags.isEnabled(Flags.REVAMPED_BOUNCER_MESSAGES)) {
+            mMessageAreaController.disable();
+        } else {
+            mMessageAreaController.setIsVisible(true);
+        }
+    }
+
+
     @Override
     protected void onViewDetached() {
     }
@@ -208,14 +225,14 @@
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
                         emergencyButtonController, mMessageAreaControllerFactory,
-                        mDevicePostureController);
+                        mDevicePostureController, mFeatureFlags);
             } else if (keyguardInputView instanceof KeyguardPasswordView) {
                 return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
-                        mFalsingCollector, mKeyguardViewController);
-
+                        mFalsingCollector, mKeyguardViewController,
+                        mFeatureFlags);
             } else if (keyguardInputView instanceof KeyguardPINView) {
                 return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
@@ -227,13 +244,13 @@
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
-                        emergencyButtonController);
+                        emergencyButtonController, mFeatureFlags);
             } else if (keyguardInputView instanceof KeyguardSimPukView) {
                 return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
-                        emergencyButtonController);
+                        emergencyButtonController, mFeatureFlags);
             }
 
             throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 553453d..fc66527 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -44,11 +44,18 @@
     private ViewGroup mContainer;
     private int mTopMargin;
     protected boolean mAnimate;
+    private final int mStyleResId;
+    private boolean mIsDisabled = false;
 
     public KeyguardMessageArea(Context context, AttributeSet attrs) {
         super(context, attrs);
         setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
-
+        if (attrs != null) {
+            mStyleResId = attrs.getStyleAttribute();
+        } else {
+            // Set to default reference style if the component is used without setting "style" attr
+            mStyleResId = R.style.Keyguard_TextView;
+        }
         onThemeChanged();
     }
 
@@ -82,13 +89,17 @@
     }
 
     void onDensityOrFontScaleChanged() {
-        TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] {
+        TypedArray array = mContext.obtainStyledAttributes(getStyleResId(), new int[] {
                 android.R.attr.textSize
         });
         setTextSize(TypedValue.COMPLEX_UNIT_PX, array.getDimensionPixelSize(0, 0));
         array.recycle();
     }
 
+    protected int getStyleResId() {
+        return mStyleResId;
+    }
+
     @Override
     public void setMessage(CharSequence msg, boolean animate) {
         if (!TextUtils.isEmpty(msg)) {
@@ -118,6 +129,10 @@
     }
 
     void update() {
+        if (mIsDisabled) {
+            setVisibility(GONE);
+            return;
+        }
         CharSequence status = mMessage;
         setVisibility(TextUtils.isEmpty(status) || (!mIsVisible) ? INVISIBLE : VISIBLE);
         setText(status);
@@ -136,4 +151,17 @@
 
     /** Set the text color */
     protected abstract void updateTextColor();
+
+    /**
+     * Mark this view with {@link android.view.View#GONE} visibility to remove this from the layout
+     * of the view. Any calls to {@link #setIsVisible(boolean)} after this will be a no-op.
+     */
+    public void disable() {
+        mIsDisabled = true;
+        update();
+    }
+
+    public boolean isDisabled() {
+        return mIsDisabled;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 99bc32a..0332c9f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -133,6 +133,14 @@
         mView.setIsVisible(isVisible);
     }
 
+    /**
+     * Mark this view with {@link View#GONE} visibility to remove this from the layout of the view.
+     * Any calls to {@link #setIsVisible(boolean)} after this will be a no-op.
+     */
+    public void disable() {
+        mView.disable();
+    }
+
     public void setMessage(CharSequence s) {
         setMessage(s, true);
     }
@@ -141,6 +149,9 @@
      * Sets a message to the underlying text view.
      */
     public void setMessage(CharSequence s, boolean animate) {
+        if (mView.isDisabled()) {
+            return;
+        }
         mView.setMessage(s, animate);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 58807e4..2377057 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -51,7 +51,7 @@
     private View[][] mViews;
     private int mYTrans;
     private int mYTransOffset;
-    private View mBouncerMessageView;
+    private View mBouncerMessageArea;
     @DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
     public static final long ANIMATION_DURATION = 650;
 
@@ -145,7 +145,7 @@
         super.onFinishInflate();
 
         mContainer = findViewById(R.id.pin_container);
-        mBouncerMessageView = findViewById(R.id.bouncer_message_area);
+        mBouncerMessageArea = findViewById(R.id.bouncer_message_area);
         mViews = new View[][]{
                 new View[]{
                         findViewById(R.id.row0), null, null
@@ -221,9 +221,9 @@
         Interpolator legacyDecelerate = Interpolators.LEGACY_DECELERATE;
         float standardProgress = standardDecelerate.getInterpolation(progress);
 
-        mBouncerMessageView.setTranslationY(
+        mBouncerMessageArea.setTranslationY(
                 mYTrans - mYTrans * standardProgress);
-        mBouncerMessageView.setAlpha(standardProgress);
+        mBouncerMessageArea.setAlpha(standardProgress);
 
         for (int i = 0; i < mViews.length; i++) {
             View[] row = mViews[i];
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index d221e22..1f6b09b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -40,6 +40,7 @@
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.util.List;
@@ -104,10 +105,11 @@
             @Main DelayableExecutor mainExecutor,
             @Main Resources resources,
             FalsingCollector falsingCollector,
-            KeyguardViewController keyguardViewController) {
+            KeyguardViewController keyguardViewController,
+            FeatureFlags featureFlags) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, falsingCollector,
-                emergencyButtonController);
+                emergencyButtonController, featureFlags);
         mKeyguardSecurityCallback = keyguardSecurityCallback;
         mInputMethodManager = inputMethodManager;
         mMainExecutor = mainExecutor;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 39225fb..64b1c50 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -38,6 +38,7 @@
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingClassifier;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 
 import java.util.HashMap;
@@ -196,9 +197,9 @@
             FalsingCollector falsingCollector,
             EmergencyButtonController emergencyButtonController,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
-            DevicePostureController postureController) {
+            DevicePostureController postureController, FeatureFlags featureFlags) {
         super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
-                messageAreaControllerFactory);
+                messageAreaControllerFactory, featureFlags);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockPatternUtils = lockPatternUtils;
         mLatencyTracker = latencyTracker;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 2339747..5cb2c5c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -34,6 +34,8 @@
 import android.view.KeyEvent;
 import android.view.View;
 
+import androidx.annotation.CallSuper;
+
 import com.android.app.animation.Interpolators;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.systemui.R;
@@ -147,7 +149,9 @@
     }
 
     @Override
+    @CallSuper
     protected void onFinishInflate() {
+        super.onFinishInflate();
         mPasswordEntry = findViewById(getPasswordTextViewId());
 
         // Set selected property on so the view can send accessibility events.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index ded1238..31cbdde 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -27,6 +27,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.flags.FeatureFlags;
 
 public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
         extends KeyguardAbsKeyInputViewController<T> {
@@ -58,10 +59,11 @@
             LatencyTracker latencyTracker,
             LiftToActivateListener liftToActivateListener,
             EmergencyButtonController emergencyButtonController,
-            FalsingCollector falsingCollector) {
+            FalsingCollector falsingCollector,
+            FeatureFlags featureFlags) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, falsingCollector,
-                emergencyButtonController);
+                emergencyButtonController, featureFlags);
         mLiftToActivateListener = liftToActivateListener;
         mFalsingCollector = falsingCollector;
         mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 1adaafb..f191281 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -56,7 +56,7 @@
             FeatureFlags featureFlags) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
-                emergencyButtonController, falsingCollector);
+                emergencyButtonController, falsingCollector, featureFlags);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mPostureController = postureController;
         mLockPatternUtils = lockPatternUtils;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
index 3fc39af..1461dbe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
@@ -30,6 +30,7 @@
     private var disableESimButton: KeyguardEsimArea? = null
 
     override fun onFinishInflate() {
+        super.onFinishInflate()
         simImageView = findViewById(R.id.keyguard_sim)
         disableESimButton = findViewById(R.id.keyguard_esim_area)
         super.onFinishInflate()
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index a16f3047..61280af 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -41,6 +41,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.flags.FeatureFlags;
 
 public class KeyguardSimPinViewController
         extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
@@ -81,10 +82,10 @@
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
             LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
             TelephonyManager telephonyManager, FalsingCollector falsingCollector,
-            EmergencyButtonController emergencyButtonController) {
+            EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
-                emergencyButtonController, falsingCollector);
+                emergencyButtonController, falsingCollector, featureFlags);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index e9405eb..49d786f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -38,6 +38,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.flags.FeatureFlags;
 
 public class KeyguardSimPukViewController
         extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
@@ -85,10 +86,10 @@
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
             LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
             TelephonyManager telephonyManager, FalsingCollector falsingCollector,
-            EmergencyButtonController emergencyButtonController) {
+            EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
-                emergencyButtonController, falsingCollector);
+                emergencyButtonController, falsingCollector, featureFlags);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusContainer.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusContainer.kt
new file mode 100644
index 0000000..d885892
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusContainer.kt
@@ -0,0 +1,24 @@
+package com.android.keyguard
+
+import android.content.Context
+import android.graphics.Canvas
+import android.util.AttributeSet
+import android.widget.LinearLayout
+
+class KeyguardStatusContainer(
+    context: Context,
+    attrs: AttributeSet,
+) : LinearLayout(context, attrs) {
+    private var drawAlpha: Int = 255
+
+    protected override fun onSetAlpha(alpha: Int): Boolean {
+        drawAlpha = alpha
+        return true
+    }
+
+    protected override fun dispatchDraw(canvas: Canvas) {
+        val restoreTo = KeyguardClockFrame.saveCanvasAlpha(this, canvas, drawAlpha)
+        super.dispatchDraw(canvas)
+        canvas.restoreToCount(restoreTo)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 2313609..553af5b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -19,6 +19,7 @@
 import static java.util.Collections.emptySet;
 
 import android.content.Context;
+import android.graphics.Canvas;
 import android.os.Build;
 import android.os.Trace;
 import android.util.AttributeSet;
@@ -47,6 +48,7 @@
     private KeyguardSliceView mKeyguardSlice;
     private View mMediaHostContainer;
 
+    private int mDrawAlpha = 255;
     private float mDarkAmount = 0;
 
     public KeyguardStatusView(Context context) {
@@ -136,30 +138,16 @@
         Trace.endSection();
     }
 
-    /**
-     * Clock content will be clipped when goes beyond bounds,
-     * so we setAlpha for all views except clock
-     */
-    public void setAlpha(float alpha, boolean excludeClock) {
-        if (!excludeClock) {
-            setAlpha(alpha);
-            return;
-        }
-        if (alpha == 1 || alpha == 0) {
-            setAlpha(alpha);
-        }
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-            if (child == mStatusViewContainer) {
-                for (int j = 0; j < mStatusViewContainer.getChildCount(); j++) {
-                    View innerChild = mStatusViewContainer.getChildAt(j);
-                    if (innerChild != mClockView) {
-                        innerChild.setAlpha(alpha);
-                    }
-                }
-            } else {
-                child.setAlpha(alpha);
-            }
-        }
+    @Override
+    protected boolean onSetAlpha(int alpha) {
+        mDrawAlpha = alpha;
+        return true;
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        int restoreTo = KeyguardClockFrame.saveCanvasAlpha(this, canvas, mDrawAlpha);
+        super.dispatchDraw(canvas);
+        canvas.restoreToCount(restoreTo);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index af47466..794eeda 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -180,8 +180,7 @@
      */
     public void setAlpha(float alpha) {
         if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
-            mView.setAlpha(alpha, true);
-            mKeyguardClockSwitchController.setAlpha(alpha);
+            mView.setAlpha(alpha);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0d4b543..ea2b385 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1460,6 +1460,14 @@
                         ErrorAuthenticationStatus error = (ErrorAuthenticationStatus) status;
                         handleFaceError(error.getMsgId(), error.getMsg());
                     } else if (status instanceof FailedAuthenticationStatus) {
+                        if (isFaceLockedOut()) {
+                            // TODO b/270090188: remove this hack when biometrics fixes this issue.
+                            // FailedAuthenticationStatus is emitted after ErrorAuthenticationStatus
+                            // for lockout error is received
+                            mLogger.d("onAuthenticationFailed called after"
+                                    + " face has been locked out");
+                            return;
+                        }
                         handleFaceAuthFailed();
                     } else if (status instanceof HelpAuthenticationStatus) {
                         HelpAuthenticationStatus helpMsg = (HelpAuthenticationStatus) status;
@@ -1968,6 +1976,13 @@
 
                 @Override
                 public void onAuthenticationFailed() {
+                    if (isFaceLockedOut()) {
+                        // TODO b/270090188: remove this hack when biometrics fixes this issue.
+                        // onAuthenticationFailed is called after onAuthenticationError
+                        // for lockout error is received
+                        mLogger.d("onAuthenticationFailed called after face has been locked out");
+                        return;
+                    }
                     handleFaceAuthFailed();
                 }
 
@@ -2621,6 +2636,14 @@
     }
 
     /**
+     * @return true if the FP sensor is non-UDFPS and the device can be unlocked using fingerprint
+     * at this moment.
+     */
+    public boolean isFingerprintAllowedInBouncer() {
+        return !isUdfpsSupported() && isUnlockingWithFingerprintAllowed();
+    }
+
+    /**
      * @return true if there's at least one sfps enrollment for the current user.
      */
     public boolean isSfpsEnrolled() {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
new file mode 100644
index 0000000..4001a4e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard.logging
+
+import androidx.annotation.IntDef
+import com.android.keyguard.CarrierTextManager.CarrierTextCallbackInfo
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.CarrierTextManagerLog
+import javax.inject.Inject
+
+/** Logger adapter for [CarrierTextManager] to add detailed messages in a [LogBuffer] */
+@SysUISingleton
+class CarrierTextManagerLogger @Inject constructor(@CarrierTextManagerLog val buffer: LogBuffer) {
+    /**
+     * This method and the methods below trace the execution of CarrierTextManager.updateCarrierText
+     */
+    fun logUpdate(numSubs: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            { int1 = numSubs },
+            { "updateCarrierText: numSubs=$int1" },
+        )
+    }
+
+    fun logUpdateLoopStart(sub: Int, simState: Int, carrierName: String) {
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                int1 = sub
+                int2 = simState
+                str1 = carrierName
+            },
+            { "┣ updateCarrierText: updating sub=$int1 simState=$int2 carrierName=$str1" },
+        )
+    }
+
+    fun logUpdateWfcCheck() {
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {},
+            { "┣ updateCarrierText: found WFC state" },
+        )
+    }
+
+    fun logUpdateFromStickyBroadcast(plmn: String?, spn: String?) {
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                str1 = plmn
+                str2 = spn
+            },
+            { "┣ updateCarrierText: getting PLMN/SPN sticky brdcst. plmn=$str1, spn=$str1" },
+        )
+    }
+
+    /** De-structures the info object so that we don't have to generate new strings */
+    fun logCallbackSentFromUpdate(info: CarrierTextCallbackInfo) {
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                str1 = "${info.carrierText}"
+                bool1 = info.anySimReady
+                bool2 = info.airplaneMode
+            },
+            {
+                "â”— updateCarrierText: " +
+                    "result=(carrierText=$str1, anySimReady=$bool1, airplaneMode=$bool2)"
+            },
+        )
+    }
+
+    /**
+     * Used to log the starting point for _why_ the carrier text is updating. In order to keep us
+     * from holding on to too many objects, we'll just use simple ints for reasons here
+     */
+    fun logUpdateCarrierTextForReason(@CarrierTextRefreshReason reason: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { int1 = reason },
+            { "refreshing carrier info for reason: ${reason.reasonMessage()}" }
+        )
+    }
+
+    companion object {
+        const val REASON_REFRESH_CARRIER_INFO = 1
+        const val REASON_ON_TELEPHONY_CAPABLE = 2
+        const val REASON_ON_SIM_STATE_CHANGED = 3
+        const val REASON_ACTIVE_DATA_SUB_CHANGED = 4
+
+        @Retention(AnnotationRetention.SOURCE)
+        @IntDef(
+            value =
+                [
+                    REASON_REFRESH_CARRIER_INFO,
+                    REASON_ON_TELEPHONY_CAPABLE,
+                    REASON_ON_SIM_STATE_CHANGED,
+                    REASON_ACTIVE_DATA_SUB_CHANGED,
+                ]
+        )
+        annotation class CarrierTextRefreshReason
+
+        private fun @receiver:CarrierTextRefreshReason Int.reasonMessage() =
+            when (this) {
+                REASON_REFRESH_CARRIER_INFO -> "REFRESH_CARRIER_INFO"
+                REASON_ON_TELEPHONY_CAPABLE -> "ON_TELEPHONY_CAPABLE"
+                REASON_ON_SIM_STATE_CHANGED -> "SIM_STATE_CHANGED"
+                REASON_ACTIVE_DATA_SUB_CHANGED -> "ACTIVE_DATA_SUB_CHANGED"
+                else -> "unknown"
+            }
+    }
+}
+
+private const val TAG = "CarrierTextManagerLog"
diff --git a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
index 76086df..403c809 100644
--- a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
@@ -31,10 +31,10 @@
 import android.hardware.biometrics.BiometricSourceType
 import android.view.View
 import androidx.core.graphics.ColorUtils
+import com.android.app.animation.Interpolators
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.settingslib.Utils
-import com.android.app.animation.Interpolators
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.log.ScreenDecorationsLogger
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -63,7 +63,7 @@
     private var cameraProtectionColor = Color.BLACK
 
     var faceScanningAnimColor = Utils.getColorAttrDefaultColor(context,
-            R.attr.wallpaperTextColorAccent)
+        com.android.internal.R.attr.materialColorPrimaryFixed)
     private var cameraProtectionAnimator: ValueAnimator? = null
     var hideOverlayRunnable: Runnable? = null
     var faceAuthSucceeded = false
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 962140f..9007279 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -20,7 +20,6 @@
 import android.app.ActivityTaskManager
 import android.content.Context
 import android.content.res.Configuration
-import android.graphics.Color
 import android.graphics.PixelFormat
 import android.graphics.PorterDuff
 import android.graphics.PorterDuffColorFilter
@@ -436,24 +435,30 @@
     fun update() {
         val isKeyguard = reason == REASON_AUTH_KEYGUARD
         if (isKeyguard) {
-            val color = context.getColor(R.color.numpad_key_color_secondary) // match bouncer color
+            val color =
+                com.android.settingslib.Utils.getColorAttrDefaultColor(
+                    context,
+                    com.android.internal.R.attr.materialColorPrimaryFixed
+                )
+            val outerRimColor =
+                com.android.settingslib.Utils.getColorAttrDefaultColor(
+                    context,
+                    com.android.internal.R.attr.materialColorPrimaryFixedDim
+                )
             val chevronFill =
                 com.android.settingslib.Utils.getColorAttrDefaultColor(
                     context,
-                    android.R.attr.textColorPrimaryInverse
+                    com.android.internal.R.attr.materialColorOnPrimaryFixed
                 )
-            for (key in listOf(".blue600", ".blue400")) {
-                addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
-                    PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
-                }
+            addValueCallback(KeyPath(".blue600", "**"), LottieProperty.COLOR_FILTER) {
+                PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
+            }
+            addValueCallback(KeyPath(".blue400", "**"), LottieProperty.COLOR_FILTER) {
+                PorterDuffColorFilter(outerRimColor, PorterDuff.Mode.SRC_ATOP)
             }
             addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
                 PorterDuffColorFilter(chevronFill, PorterDuff.Mode.SRC_ATOP)
             }
-        } else if (!isDarkMode(context)) {
-            addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
-                PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP)
-            }
         } else if (isDarkMode(context)) {
             for (key in listOf(".blue600", ".blue400")) {
                 addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
index 63f0e9d..a2840fc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
@@ -176,9 +176,9 @@
         }
 
         mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
-            android.R.attr.textColorPrimary);
+                com.android.internal.R.attr.materialColorOnSurface);
         final int backgroundColor = Utils.getColorAttrDefaultColor(getContext(),
-                com.android.internal.R.attr.colorSurface);
+                com.android.internal.R.attr.materialColorSurfaceContainerHigh);
         mBgProtection.setImageTintList(ColorStateList.valueOf(backgroundColor));
         mLockScreenFp.invalidate(); // updated with a valueCallback
     }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 4bdfc7e..340ed2e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -665,15 +665,6 @@
     val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
         unreleasedFlag(2400, "warn_on_blocking_binder_transactions")
 
-    // 2500 - output switcher
-    // TODO(b/261538825): Tracking Bug
-    @JvmField
-    val OUTPUT_SWITCHER_ADVANCED_LAYOUT = releasedFlag(2500, "output_switcher_advanced_layout")
-    @JvmField
-    val OUTPUT_SWITCHER_ROUTES_PROCESSING = releasedFlag(2501, "output_switcher_routes_processing")
-    @JvmField
-    val OUTPUT_SWITCHER_DEVICE_STATUS = releasedFlag(2502, "output_switcher_device_status")
-
     // 2700 - unfold transitions
     // TODO(b/265764985): Tracking Bug
     @Keep
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 6e77dcb..54da680 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -42,8 +42,6 @@
 import android.app.Service;
 import android.app.WindowConfiguration;
 import android.content.Intent;
-import android.graphics.Point;
-import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Debug;
@@ -77,6 +75,7 @@
 import com.android.systemui.settings.DisplayTracker;
 import com.android.wm.shell.transition.ShellTransitions;
 import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
 
 import java.util.ArrayList;
 
@@ -105,7 +104,8 @@
         }
     }
 
-    private static RemoteAnimationTarget[] wrap(TransitionInfo info, boolean wallpapers) {
+    private static RemoteAnimationTarget[] wrap(TransitionInfo info, boolean wallpapers,
+            SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
         final ArrayList<RemoteAnimationTarget> out = new ArrayList<>();
         for (int i = 0; i < info.getChanges().size(); i++) {
             boolean changeIsWallpaper =
@@ -115,32 +115,14 @@
             final TransitionInfo.Change change = info.getChanges().get(i);
             final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
             final int taskId = taskInfo != null ? change.getTaskInfo().taskId : -1;
-            boolean isNotInRecents;
-            WindowConfiguration windowConfiguration = null;
-            if (taskInfo != null) {
-                if (taskInfo.getConfiguration() != null) {
-                    windowConfiguration =
-                            change.getTaskInfo().getConfiguration().windowConfiguration;
-                }
-                isNotInRecents = !change.getTaskInfo().isRunning;
-            } else {
-                isNotInRecents = true;
-            }
-            Rect localBounds = new Rect(change.getEndAbsBounds());
-            localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);
 
-            final RemoteAnimationTarget target = new RemoteAnimationTarget(
-                    taskId,
-                    newModeToLegacyMode(change.getMode()),
-                    change.getLeash(),
-                    (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0
-                            || (change.getFlags() & TransitionInfo.FLAG_SHOW_WALLPAPER) != 0,
-                    null /* clipRect */,
-                    new Rect(0, 0, 0, 0) /* contentInsets */,
+            final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
+                    // wallpapers go into the "below" layer space
                     info.getChanges().size() - i,
-                    new Point(), localBounds, new Rect(change.getEndAbsBounds()),
-                    windowConfiguration, isNotInRecents, null /* startLeash */,
-                    change.getStartAbsBounds(), taskInfo, false /* allowEnterPip */);
+                    // keyguard treats wallpaper as translucent
+                    (change.getFlags() & TransitionInfo.FLAG_SHOW_WALLPAPER) != 0,
+                    info, t, leashMap);
+
             // Use hasAnimatingParent to mark the anything below root task
             if (taskId != -1 && change.getParent() != null) {
                 final TransitionInfo.Change parentChange = info.getChange(change.getParent());
@@ -178,31 +160,27 @@
         return new IRemoteTransition.Stub() {
             final ArrayMap<IBinder, IRemoteTransitionFinishedCallback> mFinishCallbacks =
                     new ArrayMap<>();
+            private final ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = new ArrayMap<>();
 
             @Override
             public void startAnimation(IBinder transition, TransitionInfo info,
                     SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
                     throws RemoteException {
                 Slog.d(TAG, "Starts IRemoteAnimationRunner: info=" + info);
-                final RemoteAnimationTarget[] apps = wrap(info, false /* wallpapers */);
-                final RemoteAnimationTarget[] wallpapers = wrap(info, true /* wallpapers */);
+                final RemoteAnimationTarget[] apps =
+                        wrap(info, false /* wallpapers */, t, mLeashMap);
+                final RemoteAnimationTarget[] wallpapers =
+                        wrap(info, true /* wallpapers */, t, mLeashMap);
                 final RemoteAnimationTarget[] nonApps = new RemoteAnimationTarget[0];
 
                 // Sets the alpha to 0 for the opening root task for fade in animation. And since
                 // the fade in animation can only apply on the first opening app, so set alpha to 1
                 // for anything else.
-                boolean foundOpening = false;
                 for (RemoteAnimationTarget target : apps) {
                     if (target.taskId != -1
                             && target.mode == RemoteAnimationTarget.MODE_OPENING
                             && !target.hasAnimatingParent) {
-                        if (foundOpening) {
-                            Log.w(TAG, "More than one opening target");
-                            t.setAlpha(target.leash, 1.0f);
-                            continue;
-                        }
                         t.setAlpha(target.leash, 0.0f);
-                        foundOpening = true;
                     } else {
                         t.setAlpha(target.leash, 1.0f);
                     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardBouncerMessages.kt b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt
similarity index 67%
rename from packages/SystemUI/src/com/android/keyguard/KeyguardBouncerMessages.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt
index f4145db..4085dab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardBouncerMessages.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.keyguard
+package com.android.systemui.keyguard.bouncer.data.factory
 
 import android.annotation.IntDef
+import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode
 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT
 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
@@ -34,6 +35,7 @@
 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.R.string.bouncer_face_not_recognized
 import com.android.systemui.R.string.keyguard_enter_password
 import com.android.systemui.R.string.keyguard_enter_pattern
@@ -71,10 +73,86 @@
 import com.android.systemui.R.string.kg_wrong_password_try_again
 import com.android.systemui.R.string.kg_wrong_pattern_try_again
 import com.android.systemui.R.string.kg_wrong_pin_try_again
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
+import com.android.systemui.keyguard.bouncer.shared.model.Message
+import javax.inject.Inject
 
-typealias BouncerMessage = Pair<Int, Int>
+@SysUISingleton
+class BouncerMessageFactory
+@Inject
+constructor(
+    private val updateMonitor: KeyguardUpdateMonitor,
+    private val securityModel: KeyguardSecurityModel,
+) {
 
-fun emptyBouncerMessage(): BouncerMessage = Pair(0, 0)
+    fun createFromPromptReason(
+        @BouncerPromptReason reason: Int,
+        userId: Int,
+    ): BouncerMessageModel? {
+        val pair =
+            getBouncerMessage(
+                reason,
+                securityModel.getSecurityMode(userId),
+                updateMonitor.isFingerprintAllowedInBouncer
+            )
+        return pair?.let {
+            BouncerMessageModel(
+                message = Message(messageResId = pair.first),
+                secondaryMessage = Message(messageResId = pair.second)
+            )
+        }
+    }
+
+    fun createFromString(
+        primaryMsg: String? = null,
+        secondaryMsg: String? = null
+    ): BouncerMessageModel =
+        BouncerMessageModel(
+            message = primaryMsg?.let { Message(message = it) },
+            secondaryMessage = secondaryMsg?.let { Message(message = it) },
+        )
+
+    /**
+     * Helper method that provides the relevant bouncer message that should be shown for different
+     * scenarios indicated by [reason]. [securityMode] & [fpAllowedInBouncer] parameters are used to
+     * provide a more specific message.
+     */
+    private fun getBouncerMessage(
+        @BouncerPromptReason reason: Int,
+        securityMode: SecurityMode,
+        fpAllowedInBouncer: Boolean = false
+    ): Pair<Int, Int>? {
+        return when (reason) {
+            PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode)
+            PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode)
+            PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode)
+            PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode)
+            PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode)
+            PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode)
+            PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode)
+            PROMPT_REASON_FACE_LOCKED_OUT -> faceUnlockUnavailable(securityMode)
+            PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT ->
+                if (fpAllowedInBouncer) incorrectSecurityInputWithFingerprint(securityMode)
+                else incorrectSecurityInput(securityMode)
+            PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT ->
+                if (fpAllowedInBouncer) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
+                else nonStrongAuthTimeout(securityMode)
+            PROMPT_REASON_TRUSTAGENT_EXPIRED ->
+                if (fpAllowedInBouncer) trustAgentDisabledWithFingerprintAllowed(securityMode)
+                else trustAgentDisabled(securityMode)
+            PROMPT_REASON_INCORRECT_FACE_INPUT ->
+                if (fpAllowedInBouncer) incorrectFaceInputWithFingerprintAllowed(securityMode)
+                else incorrectFaceInput(securityMode)
+            PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode)
+            PROMPT_REASON_DEFAULT ->
+                if (fpAllowedInBouncer) defaultMessageWithFingerprint(securityMode)
+                else defaultMessage(securityMode)
+            PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode)
+            else -> null
+        }
+    }
+}
 
 @Retention(AnnotationRetention.SOURCE)
 @IntDef(
@@ -97,48 +175,7 @@
 )
 annotation class BouncerPromptReason
 
-/**
- * Helper method that provides the relevant bouncer message that should be shown for different
- * scenarios indicated by [reason]. [securityMode] & [fpAllowedInBouncer] parameters are used to
- * provide a more specific message.
- */
-@JvmOverloads
-fun getBouncerMessage(
-    @BouncerPromptReason reason: Int,
-    securityMode: SecurityMode,
-    fpAllowedInBouncer: Boolean = false
-): BouncerMessage {
-    return when (reason) {
-        PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode)
-        PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode)
-        PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode)
-        PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode)
-        PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode)
-        PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode)
-        PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode)
-        PROMPT_REASON_FACE_LOCKED_OUT -> faceUnlockUnavailable(securityMode)
-        PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT ->
-            if (fpAllowedInBouncer) incorrectSecurityInputWithFingerprint(securityMode)
-            else incorrectSecurityInput(securityMode)
-        PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT ->
-            if (fpAllowedInBouncer) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
-            else nonStrongAuthTimeout(securityMode)
-        PROMPT_REASON_TRUSTAGENT_EXPIRED ->
-            if (fpAllowedInBouncer) trustAgentDisabledWithFingerprintAllowed(securityMode)
-            else trustAgentDisabled(securityMode)
-        PROMPT_REASON_INCORRECT_FACE_INPUT ->
-            if (fpAllowedInBouncer) incorrectFaceInputWithFingerprintAllowed(securityMode)
-            else incorrectFaceInput(securityMode)
-        PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode)
-        PROMPT_REASON_DEFAULT ->
-            if (fpAllowedInBouncer) defaultMessageWithFingerprint(securityMode)
-            else defaultMessage(securityMode)
-        PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode)
-        else -> emptyBouncerMessage()
-    }
-}
-
-fun defaultMessage(securityMode: SecurityMode): BouncerMessage {
+private fun defaultMessage(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0)
         SecurityMode.Password -> Pair(keyguard_enter_password, 0)
@@ -147,7 +184,7 @@
     }
 }
 
-fun defaultMessageWithFingerprint(securityMode: SecurityMode): BouncerMessage {
+private fun defaultMessageWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, 0)
         SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, 0)
@@ -156,7 +193,7 @@
     }
 }
 
-fun incorrectSecurityInput(securityMode: SecurityMode): BouncerMessage {
+private fun incorrectSecurityInput(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, 0)
         SecurityMode.Password -> Pair(kg_wrong_password_try_again, 0)
@@ -165,7 +202,7 @@
     }
 }
 
-fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): BouncerMessage {
+private fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, kg_wrong_input_try_fp_suggestion)
         SecurityMode.Password -> Pair(kg_wrong_password_try_again, kg_wrong_input_try_fp_suggestion)
@@ -174,7 +211,7 @@
     }
 }
 
-fun incorrectFingerprintInput(securityMode: SecurityMode): BouncerMessage {
+private fun incorrectFingerprintInput(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pattern)
         SecurityMode.Password -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_password)
@@ -183,7 +220,7 @@
     }
 }
 
-fun incorrectFaceInput(securityMode: SecurityMode): BouncerMessage {
+private fun incorrectFaceInput(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pattern)
         SecurityMode.Password -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_password)
@@ -192,7 +229,7 @@
     }
 }
 
-fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessage {
+private fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, bouncer_face_not_recognized)
         SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, bouncer_face_not_recognized)
@@ -201,7 +238,7 @@
     }
 }
 
-fun biometricLockout(securityMode: SecurityMode): BouncerMessage {
+private fun biometricLockout(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern)
         SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password)
@@ -210,7 +247,7 @@
     }
 }
 
-fun authRequiredAfterReboot(securityMode: SecurityMode): BouncerMessage {
+private fun authRequiredAfterReboot(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_reason_restart_pattern)
         SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_reason_restart_password)
@@ -219,7 +256,7 @@
     }
 }
 
-fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): BouncerMessage {
+private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_dpm_lock)
         SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_dpm_lock)
@@ -228,7 +265,7 @@
     }
 }
 
-fun authRequiredAfterUserLockdown(securityMode: SecurityMode): BouncerMessage {
+private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern)
         SecurityMode.Password ->
@@ -238,7 +275,7 @@
     }
 }
 
-fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): BouncerMessage {
+private fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_unattended_update)
         SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_unattended_update)
@@ -247,7 +284,7 @@
     }
 }
 
-fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): BouncerMessage {
+private fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout)
         SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_password_auth_timeout)
@@ -256,7 +293,7 @@
     }
 }
 
-fun nonStrongAuthTimeout(securityMode: SecurityMode): BouncerMessage {
+private fun nonStrongAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_auth_timeout)
         SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_auth_timeout)
@@ -265,7 +302,7 @@
     }
 }
 
-fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessage {
+private fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_prompt_auth_timeout)
         SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_prompt_auth_timeout)
@@ -274,7 +311,7 @@
     }
 }
 
-fun faceUnlockUnavailable(securityMode: SecurityMode): BouncerMessage {
+private fun faceUnlockUnavailable(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out)
         SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out)
@@ -283,7 +320,7 @@
     }
 }
 
-fun fingerprintUnlockUnavailable(securityMode: SecurityMode): BouncerMessage {
+private fun fingerprintUnlockUnavailable(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_fp_locked_out)
         SecurityMode.Password -> Pair(keyguard_enter_password, kg_fp_locked_out)
@@ -292,7 +329,7 @@
     }
 }
 
-fun trustAgentDisabled(securityMode: SecurityMode): BouncerMessage {
+private fun trustAgentDisabled(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_trust_agent_disabled)
         SecurityMode.Password -> Pair(keyguard_enter_password, kg_trust_agent_disabled)
@@ -301,7 +338,7 @@
     }
 }
 
-fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessage {
+private fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_trust_agent_disabled)
         SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_trust_agent_disabled)
@@ -310,7 +347,7 @@
     }
 }
 
-fun primaryAuthLockedOut(securityMode: SecurityMode): BouncerMessage {
+private fun primaryAuthLockedOut(securityMode: SecurityMode): Pair<Int, Int> {
     return when (securityMode) {
         SecurityMode.Pattern ->
             Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pattern)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepository.kt
new file mode 100644
index 0000000..c4400bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepository.kt
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.bouncer.data.repository
+
+import android.hardware.biometrics.BiometricSourceType
+import android.hardware.biometrics.BiometricSourceType.FACE
+import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FACE_LOCKED_OUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FINGERPRINT_LOCKED_OUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FACE_INPUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.bouncer.data.factory.BouncerMessageFactory
+import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.TrustRepository
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/** Provide different sources of messages that needs to be shown on the bouncer. */
+interface BouncerMessageRepository {
+    /**
+     * Messages that are shown in response to the incorrect security attempts on the bouncer and
+     * primary authentication method being locked out, along with countdown messages before primary
+     * auth is active again.
+     */
+    val primaryAuthMessage: Flow<BouncerMessageModel?>
+
+    /**
+     * Help messages that are shown to the user on how to successfully perform authentication using
+     * face.
+     */
+    val faceAcquisitionMessage: Flow<BouncerMessageModel?>
+
+    /**
+     * Help messages that are shown to the user on how to successfully perform authentication using
+     * fingerprint.
+     */
+    val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?>
+
+    /** Custom message that is displayed when the bouncer is being shown to launch an app. */
+    val customMessage: Flow<BouncerMessageModel?>
+
+    /**
+     * Messages that are shown in response to biometric authentication attempts through face or
+     * fingerprint.
+     */
+    val biometricAuthMessage: Flow<BouncerMessageModel?>
+
+    /** Messages that are shown when certain auth flags are set. */
+    val authFlagsMessage: Flow<BouncerMessageModel?>
+
+    /** Messages that are show after biometrics are locked out temporarily or permanently */
+    val biometricLockedOutMessage: Flow<BouncerMessageModel?>
+
+    /** Set the value for [primaryAuthMessage] */
+    fun setPrimaryAuthMessage(value: BouncerMessageModel?)
+
+    /** Set the value for [faceAcquisitionMessage] */
+    fun setFaceAcquisitionMessage(value: BouncerMessageModel?)
+    /** Set the value for [fingerprintAcquisitionMessage] */
+    fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?)
+
+    /** Set the value for [customMessage] */
+    fun setCustomMessage(value: BouncerMessageModel?)
+
+    /**
+     * Clear any previously set messages for [primaryAuthMessage], [faceAcquisitionMessage],
+     * [fingerprintAcquisitionMessage] & [customMessage]
+     */
+    fun clearMessage()
+}
+
+@SysUISingleton
+class BouncerMessageRepositoryImpl
+@Inject
+constructor(
+    trustRepository: TrustRepository,
+    biometricSettingsRepository: BiometricSettingsRepository,
+    updateMonitor: KeyguardUpdateMonitor,
+    private val bouncerMessageFactory: BouncerMessageFactory,
+    private val userRepository: UserRepository,
+    fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+) : BouncerMessageRepository {
+
+    private val isFaceEnrolledAndEnabled =
+        and(
+            biometricSettingsRepository.isFaceAuthenticationEnabled,
+            biometricSettingsRepository.isFaceEnrolled
+        )
+
+    private val isFingerprintEnrolledAndEnabled =
+        and(
+            biometricSettingsRepository.isFingerprintEnabledByDevicePolicy,
+            biometricSettingsRepository.isFingerprintEnrolled
+        )
+
+    private val isAnyBiometricsEnabledAndEnrolled =
+        or(isFaceEnrolledAndEnabled, isFingerprintEnrolledAndEnabled)
+
+    private val authFlagsBasedPromptReason: Flow<Int> =
+        combine(
+                biometricSettingsRepository.authenticationFlags,
+                trustRepository.isCurrentUserTrustManaged,
+                isAnyBiometricsEnabledAndEnrolled,
+                ::Triple
+            )
+            .map { (flags, isTrustManaged, biometricsEnrolledAndEnabled) ->
+                val trustOrBiometricsAvailable = (isTrustManaged || biometricsEnrolledAndEnabled)
+                return@map if (
+                    trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterReboot
+                ) {
+                    PROMPT_REASON_RESTART
+                } else if (trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterTimeout) {
+                    PROMPT_REASON_TIMEOUT
+                } else if (flags.isPrimaryAuthRequiredAfterDpmLockdown) {
+                    PROMPT_REASON_DEVICE_ADMIN
+                } else if (isTrustManaged && flags.someAuthRequiredAfterUserRequest) {
+                    PROMPT_REASON_TRUSTAGENT_EXPIRED
+                } else if (isTrustManaged && flags.someAuthRequiredAfterTrustAgentExpired) {
+                    PROMPT_REASON_TRUSTAGENT_EXPIRED
+                } else if (trustOrBiometricsAvailable && flags.isInUserLockdown) {
+                    PROMPT_REASON_USER_REQUEST
+                } else if (
+                    trustOrBiometricsAvailable && flags.primaryAuthRequiredForUnattendedUpdate
+                ) {
+                    PROMPT_REASON_PREPARE_FOR_UPDATE
+                } else if (
+                    trustOrBiometricsAvailable &&
+                        flags.strongerAuthRequiredAfterNonStrongBiometricsTimeout
+                ) {
+                    PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
+                } else {
+                    PROMPT_REASON_NONE
+                }
+            }
+
+    private val biometricAuthReason: Flow<Int> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : KeyguardUpdateMonitorCallback() {
+                        override fun onBiometricAuthFailed(
+                            biometricSourceType: BiometricSourceType?
+                        ) {
+                            val promptReason =
+                                if (biometricSourceType == FINGERPRINT)
+                                    PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
+                                else if (
+                                    biometricSourceType == FACE && !updateMonitor.isFaceLockedOut
+                                ) {
+                                    PROMPT_REASON_INCORRECT_FACE_INPUT
+                                } else PROMPT_REASON_NONE
+                            trySendWithFailureLogging(promptReason, TAG, "onBiometricAuthFailed")
+                        }
+
+                        override fun onBiometricsCleared() {
+                            trySendWithFailureLogging(
+                                PROMPT_REASON_NONE,
+                                TAG,
+                                "onBiometricsCleared"
+                            )
+                        }
+
+                        override fun onBiometricAcquired(
+                            biometricSourceType: BiometricSourceType?,
+                            acquireInfo: Int
+                        ) {
+                            trySendWithFailureLogging(
+                                PROMPT_REASON_NONE,
+                                TAG,
+                                "clearBiometricPrompt for new auth session."
+                            )
+                        }
+
+                        override fun onBiometricAuthenticated(
+                            userId: Int,
+                            biometricSourceType: BiometricSourceType?,
+                            isStrongBiometric: Boolean
+                        ) {
+                            trySendWithFailureLogging(
+                                PROMPT_REASON_NONE,
+                                TAG,
+                                "onBiometricAuthenticated"
+                            )
+                        }
+                    }
+                updateMonitor.registerCallback(callback)
+                awaitClose { updateMonitor.removeCallback(callback) }
+            }
+            .distinctUntilChanged()
+
+    private val _primaryAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
+    override val primaryAuthMessage: Flow<BouncerMessageModel?> = _primaryAuthMessage
+
+    private val _faceAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
+    override val faceAcquisitionMessage: Flow<BouncerMessageModel?> = _faceAcquisitionMessage
+
+    private val _fingerprintAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
+    override val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?> =
+        _fingerprintAcquisitionMessage
+
+    private val _customMessage = MutableStateFlow<BouncerMessageModel?>(null)
+    override val customMessage: Flow<BouncerMessageModel?> = _customMessage
+
+    override val biometricAuthMessage: Flow<BouncerMessageModel?> =
+        biometricAuthReason
+            .map {
+                if (it == PROMPT_REASON_NONE) null
+                else
+                    bouncerMessageFactory.createFromPromptReason(
+                        it,
+                        userRepository.getSelectedUserInfo().id
+                    )
+            }
+            .onStart { emit(null) }
+            .distinctUntilChanged()
+
+    override val authFlagsMessage: Flow<BouncerMessageModel?> =
+        authFlagsBasedPromptReason
+            .map {
+                if (it == PROMPT_REASON_NONE) null
+                else
+                    bouncerMessageFactory.createFromPromptReason(
+                        it,
+                        userRepository.getSelectedUserInfo().id
+                    )
+            }
+            .onStart { emit(null) }
+            .distinctUntilChanged()
+
+    // TODO (b/262838215): Replace with DeviceEntryFaceAuthRepository when the new face auth system
+    // has been launched.
+    private val faceLockedOut: Flow<Boolean> = conflatedCallbackFlow {
+        val callback =
+            object : KeyguardUpdateMonitorCallback() {
+                override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType?) {
+                    if (biometricSourceType == FACE) {
+                        trySendWithFailureLogging(
+                            updateMonitor.isFaceLockedOut,
+                            TAG,
+                            "face lock out state changed."
+                        )
+                    }
+                }
+            }
+        updateMonitor.registerCallback(callback)
+        trySendWithFailureLogging(updateMonitor.isFaceLockedOut, TAG, "face lockout initial value")
+        awaitClose { updateMonitor.removeCallback(callback) }
+    }
+
+    override val biometricLockedOutMessage: Flow<BouncerMessageModel?> =
+        combine(fingerprintAuthRepository.isLockedOut, faceLockedOut) { fp, face ->
+            return@combine if (fp) {
+                bouncerMessageFactory.createFromPromptReason(
+                    PROMPT_REASON_FINGERPRINT_LOCKED_OUT,
+                    userRepository.getSelectedUserInfo().id
+                )
+            } else if (face) {
+                bouncerMessageFactory.createFromPromptReason(
+                    PROMPT_REASON_FACE_LOCKED_OUT,
+                    userRepository.getSelectedUserInfo().id
+                )
+            } else null
+        }
+
+    override fun setPrimaryAuthMessage(value: BouncerMessageModel?) {
+        _primaryAuthMessage.value = value
+    }
+
+    override fun setFaceAcquisitionMessage(value: BouncerMessageModel?) {
+        _faceAcquisitionMessage.value = value
+    }
+
+    override fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) {
+        _fingerprintAcquisitionMessage.value = value
+    }
+
+    override fun setCustomMessage(value: BouncerMessageModel?) {
+        _customMessage.value = value
+    }
+
+    override fun clearMessage() {
+        _fingerprintAcquisitionMessage.value = null
+        _faceAcquisitionMessage.value = null
+        _primaryAuthMessage.value = null
+        _customMessage.value = null
+    }
+
+    companion object {
+        const val TAG = "BouncerDetailedMessageRepository"
+    }
+}
+
+private fun and(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) =
+    flow.combine(anotherFlow) { a, b -> a && b }
+
+private fun or(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) =
+    flow.combine(anotherFlow) { a, b -> a || b }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageAuditLogger.kt
new file mode 100644
index 0000000..56f81fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageAuditLogger.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.bouncer.domain.interactor
+
+import android.os.Build
+import android.util.Log
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository
+import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+private val TAG = BouncerMessageAuditLogger::class.simpleName!!
+
+/** Logger that echoes bouncer messages state to logcat in debuggable builds. */
+@SysUISingleton
+class BouncerMessageAuditLogger
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    private val repository: BouncerMessageRepository,
+    private val interactor: BouncerMessageInteractor,
+) : CoreStartable {
+    override fun start() {
+        if (Build.isDebuggable()) {
+            collectAndLog(repository.biometricAuthMessage, "biometricMessage: ")
+            collectAndLog(repository.primaryAuthMessage, "primaryAuthMessage: ")
+            collectAndLog(repository.customMessage, "customMessage: ")
+            collectAndLog(repository.faceAcquisitionMessage, "faceAcquisitionMessage: ")
+            collectAndLog(
+                repository.fingerprintAcquisitionMessage,
+                "fingerprintAcquisitionMessage: "
+            )
+            collectAndLog(repository.authFlagsMessage, "authFlagsMessage: ")
+            collectAndLog(interactor.bouncerMessage, "interactor.bouncerMessage: ")
+        }
+    }
+
+    private fun collectAndLog(flow: Flow<BouncerMessageModel?>, context: String) {
+        scope.launch { flow.collect { Log.d(TAG, context + it) } }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractor.kt
new file mode 100644
index 0000000..1754d93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.bouncer.domain.interactor
+
+import android.os.CountDownTimer
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
+import com.android.systemui.keyguard.bouncer.data.factory.BouncerMessageFactory
+import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository
+import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+import kotlin.math.roundToInt
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class BouncerMessageInteractor
+@Inject
+constructor(
+    private val repository: BouncerMessageRepository,
+    private val factory: BouncerMessageFactory,
+    private val userRepository: UserRepository,
+    private val countDownTimerUtil: CountDownTimerUtil,
+    private val featureFlags: FeatureFlags,
+) {
+    fun onPrimaryAuthLockedOut(secondsBeforeLockoutReset: Long) {
+        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+
+        val callback =
+            object : CountDownTimerCallback {
+                override fun onFinish() {
+                    repository.clearMessage()
+                }
+
+                override fun onTick(millisUntilFinished: Long) {
+                    val secondsRemaining = (millisUntilFinished / 1000.0).roundToInt()
+                    val message =
+                        factory.createFromPromptReason(
+                            reason = PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT,
+                            userId = userRepository.getSelectedUserInfo().id
+                        )
+                    message?.message?.animate = false
+                    message?.message?.formatterArgs =
+                        mutableMapOf<String, Any>(Pair("count", secondsRemaining))
+                    repository.setPrimaryAuthMessage(message)
+                }
+            }
+        countDownTimerUtil.startNewTimer(secondsBeforeLockoutReset * 1000, 1000, callback)
+    }
+
+    fun onPrimaryAuthIncorrectAttempt() {
+        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+
+        repository.setPrimaryAuthMessage(
+            factory.createFromPromptReason(
+                PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
+                userRepository.getSelectedUserInfo().id
+            )
+        )
+    }
+
+    fun setFingerprintAcquisitionMessage(value: String?) {
+        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+
+        repository.setFingerprintAcquisitionMessage(
+            if (value != null) {
+                factory.createFromString(secondaryMsg = value)
+            } else {
+                null
+            }
+        )
+    }
+
+    fun setFaceAcquisitionMessage(value: String?) {
+        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+
+        repository.setFaceAcquisitionMessage(
+            if (value != null) {
+                factory.createFromString(secondaryMsg = value)
+            } else {
+                null
+            }
+        )
+    }
+
+    fun setCustomMessage(value: String?) {
+        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+
+        repository.setCustomMessage(
+            if (value != null) {
+                factory.createFromString(secondaryMsg = value)
+            } else {
+                null
+            }
+        )
+    }
+
+    fun onPrimaryBouncerUserInput() {
+        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+
+        repository.clearMessage()
+    }
+
+    fun onBouncerBeingHidden() {
+        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+
+        repository.clearMessage()
+    }
+
+    private fun firstNonNullMessage(
+        oneMessageModel: Flow<BouncerMessageModel?>,
+        anotherMessageModel: Flow<BouncerMessageModel?>
+    ): Flow<BouncerMessageModel?> {
+        return oneMessageModel.combine(anotherMessageModel) { a, b -> a ?: b }
+    }
+
+    // Null if feature flag is enabled which gets ignored always or empty bouncer message model that
+    // always maps to an empty string.
+    private fun nullOrEmptyMessage() =
+        flowOf(
+            if (featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) null
+            else factory.createFromString("", "")
+        )
+
+    val bouncerMessage =
+        listOf(
+                nullOrEmptyMessage(),
+                repository.primaryAuthMessage,
+                repository.biometricAuthMessage,
+                repository.fingerprintAcquisitionMessage,
+                repository.faceAcquisitionMessage,
+                repository.customMessage,
+                repository.authFlagsMessage,
+                repository.biometricLockedOutMessage,
+                userRepository.selectedUserInfo.map {
+                    factory.createFromPromptReason(PROMPT_REASON_DEFAULT, it.id)
+                },
+            )
+            .reduce(::firstNonNullMessage)
+            .distinctUntilChanged()
+}
+
+interface CountDownTimerCallback {
+    fun onFinish()
+    fun onTick(millisUntilFinished: Long)
+}
+
+@SysUISingleton
+open class CountDownTimerUtil @Inject constructor() {
+
+    /**
+     * Start a new count down timer that runs for [millisInFuture] with a tick every
+     * [millisInterval]
+     */
+    fun startNewTimer(
+        millisInFuture: Long,
+        millisInterval: Long,
+        callback: CountDownTimerCallback,
+    ): CountDownTimer {
+        return object : CountDownTimer(millisInFuture, millisInterval) {
+                override fun onFinish() = callback.onFinish()
+
+                override fun onTick(millisUntilFinished: Long) =
+                    callback.onTick(millisUntilFinished)
+            }
+            .start()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/shared/model/BouncerMessageModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/shared/model/BouncerMessageModel.kt
new file mode 100644
index 0000000..46e8873
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/shared/model/BouncerMessageModel.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.bouncer.shared.model
+
+import android.content.res.ColorStateList
+
+/**
+ * Represents the message displayed on the bouncer. It has two parts, primary and a secondary
+ * message
+ */
+data class BouncerMessageModel(val message: Message? = null, val secondaryMessage: Message? = null)
+
+/**
+ * Representation of a single message on the bouncer. It can be either a string or a string resource
+ * ID
+ */
+data class Message(
+    val message: String? = null,
+    val messageResId: Int? = null,
+    val colorState: ColorStateList? = null,
+    /** Any plural formatter arguments that can used to format the [messageResId] */
+    var formatterArgs: Map<String, Any>? = null,
+    /** Specifies whether this text should be animated when it is shown. */
+    var animate: Boolean = true,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt
new file mode 100644
index 0000000..c0a5a51
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.bouncer.ui
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+import com.android.keyguard.BouncerKeyguardMessageArea
+import com.android.keyguard.KeyguardMessageArea
+import com.android.keyguard.KeyguardMessageAreaController
+import com.android.systemui.R
+
+class BouncerMessageView : LinearLayout {
+    constructor(context: Context?) : super(context)
+
+    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
+
+    init {
+        inflate(context, R.layout.bouncer_message_view, this)
+    }
+
+    var primaryMessageView: BouncerKeyguardMessageArea? = null
+    var secondaryMessageView: BouncerKeyguardMessageArea? = null
+    var primaryMessage: KeyguardMessageAreaController<KeyguardMessageArea>? = null
+    var secondaryMessage: KeyguardMessageAreaController<KeyguardMessageArea>? = null
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        primaryMessageView = findViewById(R.id.bouncer_primary_message_area)
+        secondaryMessageView = findViewById(R.id.bouncer_secondary_message_area)
+    }
+
+    fun init(factory: KeyguardMessageAreaController.Factory) {
+        primaryMessage = factory.create(primaryMessageView)
+        primaryMessage?.init()
+        secondaryMessage = factory.create(secondaryMessageView)
+        secondaryMessage?.init()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 0055f9a..0b6c7c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -25,7 +25,6 @@
 import android.os.UserHandle
 import android.util.Log
 import com.android.internal.widget.LockPatternUtils
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
 import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.biometrics.AuthController
@@ -36,13 +35,14 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.TAG
+import com.android.systemui.keyguard.shared.model.AuthenticationFlags
 import com.android.systemui.keyguard.shared.model.DevicePosture
 import com.android.systemui.user.data.repository.UserRepository
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -108,10 +108,14 @@
      * lockdown.
      */
     val isCurrentUserInLockdown: Flow<Boolean>
+
+    /** Authentication flags set for the current user. */
+    val authenticationFlags: Flow<AuthenticationFlags>
 }
 
-const val TAG = "BiometricsRepositoryImpl"
+private const val TAG = "BiometricsRepositoryImpl"
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class BiometricSettingsRepositoryImpl
 @Inject
@@ -129,6 +133,8 @@
     dumpManager: DumpManager,
 ) : BiometricSettingsRepository, Dumpable {
 
+    private val biometricsEnabledForUser = mutableMapOf<Int, Boolean>()
+
     override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
 
     private val strongAuthTracker = StrongAuthTracker(userRepository, context)
@@ -136,6 +142,9 @@
     override val isCurrentUserInLockdown: Flow<Boolean> =
         strongAuthTracker.currentUserAuthFlags.map { it.isInUserLockdown }
 
+    override val authenticationFlags: Flow<AuthenticationFlags> =
+        strongAuthTracker.currentUserAuthFlags
+
     init {
         Log.d(TAG, "Registering StrongAuthTracker")
         lockPatternUtils.registerStrongAuthTracker(strongAuthTracker)
@@ -231,9 +240,14 @@
             }
         }
 
+    private val isFaceEnabledByBiometricsManagerForCurrentUser: Flow<Boolean> =
+        userRepository.selectedUserInfo.flatMapLatest { userInfo ->
+            isFaceEnabledByBiometricsManager.map { biometricsEnabledForUser[userInfo.id] ?: false }
+        }
+
     override val isFaceAuthenticationEnabled: Flow<Boolean>
         get() =
-            combine(isFaceEnabledByBiometricsManager, isFaceEnabledByDevicePolicy) {
+            combine(isFaceEnabledByBiometricsManagerForCurrentUser, isFaceEnabledByDevicePolicy) {
                 biometricsManagerSetting,
                 devicePolicySetting ->
                 biometricsManagerSetting && devicePolicySetting
@@ -249,13 +263,13 @@
             .flowOn(backgroundDispatcher)
             .distinctUntilChanged()
 
-    private val isFaceEnabledByBiometricsManager =
+    private val isFaceEnabledByBiometricsManager: Flow<Pair<Int, Boolean>> =
         conflatedCallbackFlow {
                 val callback =
                     object : IBiometricEnabledOnKeyguardCallback.Stub() {
                         override fun onChanged(enabled: Boolean, userId: Int) {
                             trySendWithFailureLogging(
-                                enabled,
+                                Pair(userId, enabled),
                                 TAG,
                                 "biometricsEnabled state changed"
                             )
@@ -264,9 +278,10 @@
                 biometricManager?.registerEnabledOnKeyguardCallback(callback)
                 awaitClose {}
             }
+            .onEach { biometricsEnabledForUser[it.first] = it.second }
             // This is because the callback is binder-based and we want to avoid multiple callbacks
             // being registered.
-            .stateIn(scope, SharingStarted.Eagerly, false)
+            .stateIn(scope, SharingStarted.Eagerly, Pair(0, false))
 
     override val isStrongBiometricAllowed: StateFlow<Boolean> =
         strongAuthTracker.isStrongBiometricAllowed.stateIn(
@@ -306,14 +321,13 @@
             )
 }
 
+@OptIn(ExperimentalCoroutinesApi::class)
 private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
     LockPatternUtils.StrongAuthTracker(context) {
 
     // Backing field for onStrongAuthRequiredChanged
-    private val _strongAuthFlags =
-        MutableStateFlow(
-            StrongAuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId))
-        )
+    private val _authFlags =
+        MutableStateFlow(AuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId)))
 
     // Backing field for onIsNonStrongBiometricAllowedChanged
     private val _nonStrongBiometricAllowed =
@@ -321,17 +335,15 @@
             Pair(currentUserId, isNonStrongBiometricAllowedAfterIdleTimeout(currentUserId))
         )
 
-    val currentUserAuthFlags: Flow<StrongAuthenticationFlags> =
+    val currentUserAuthFlags: Flow<AuthenticationFlags> =
         userRepository.selectedUserInfo
             .map { it.id }
             .distinctUntilChanged()
             .flatMapLatest { userId ->
-                _strongAuthFlags
-                    .filter { it.userId == userId }
+                _authFlags
+                    .map { AuthenticationFlags(userId, getStrongAuthForUser(userId)) }
                     .onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
-                    .onStart {
-                        emit(StrongAuthenticationFlags(userId, getStrongAuthForUser(userId)))
-                    }
+                    .onStart { emit(AuthenticationFlags(userId, getStrongAuthForUser(userId))) }
             }
 
     /** isStrongBiometricAllowed for the current user. */
@@ -356,7 +368,7 @@
 
     override fun onStrongAuthRequiredChanged(userId: Int) {
         val newFlags = getStrongAuthForUser(userId)
-        _strongAuthFlags.value = StrongAuthenticationFlags(userId, newFlags)
+        _authFlags.value = AuthenticationFlags(userId, newFlags)
         Log.d(TAG, "onStrongAuthRequiredChanged for userId: $userId, flag value: $newFlags")
     }
 
@@ -375,11 +387,3 @@
 
 private fun DevicePolicyManager.isNotActive(userId: Int, policy: Int): Boolean =
     (getKeyguardDisabledFeatures(null, userId) and policy) == 0
-
-private data class StrongAuthenticationFlags(val userId: Int, val flag: Int) {
-    val isInUserLockdown = containsFlag(flag, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN)
-}
-
-private fun containsFlag(haystack: Int, needle: Int): Boolean {
-    return haystack and needle != 0
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
index 7c14280..5d15e69 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -22,7 +22,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
 import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
-import com.android.systemui.log.dagger.BouncerLog
+import com.android.systemui.log.dagger.BouncerTableLog
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.util.time.SystemClock
@@ -105,7 +105,7 @@
 constructor(
     private val clock: SystemClock,
     @Application private val applicationScope: CoroutineScope,
-    @BouncerLog private val buffer: TableLogBuffer,
+    @BouncerTableLog private val buffer: TableLogBuffer,
 ) : KeyguardBouncerRepository {
     /** Values associated with the PrimaryBouncer (pin/pattern/password) input. */
     private val _primaryBouncerShow = MutableStateFlow(false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
index e7b9af6..4055fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
@@ -16,8 +16,14 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import com.android.systemui.CoreStartable
+import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository
+import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepositoryImpl
+import com.android.systemui.keyguard.bouncer.domain.interactor.BouncerMessageAuditLogger
 import dagger.Binds
 import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
 
 @Module
 interface KeyguardRepositoryModule {
@@ -46,5 +52,13 @@
     @Binds
     fun keyguardBouncerRepository(impl: KeyguardBouncerRepositoryImpl): KeyguardBouncerRepository
 
+    @Binds
+    fun bouncerMessageRepository(impl: BouncerMessageRepositoryImpl): BouncerMessageRepository
+
+    @Binds
+    @IntoMap
+    @ClassKey(BouncerMessageAuditLogger::class)
+    fun bind(impl: BouncerMessageAuditLogger): CoreStartable
+
     @Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AuthenticationFlags.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AuthenticationFlags.kt
new file mode 100644
index 0000000..cf5b88f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AuthenticationFlags.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import com.android.internal.widget.LockPatternUtils
+
+/** Authentication flags corresponding to a user. */
+data class AuthenticationFlags(val userId: Int, val flag: Int) {
+    val isInUserLockdown =
+        containsFlag(
+            flag,
+            LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
+        )
+
+    val isPrimaryAuthRequiredAfterReboot =
+        containsFlag(flag, LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT)
+
+    val isPrimaryAuthRequiredAfterTimeout =
+        containsFlag(flag, LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT)
+
+    val isPrimaryAuthRequiredAfterDpmLockdown =
+        containsFlag(
+            flag,
+            LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW
+        )
+
+    val someAuthRequiredAfterUserRequest =
+        containsFlag(flag, LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST)
+
+    val someAuthRequiredAfterTrustAgentExpired =
+        containsFlag(
+            flag,
+            LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED
+        )
+
+    val primaryAuthRequiredForUnattendedUpdate =
+        containsFlag(
+            flag,
+            LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
+        )
+
+    /** Either Class 3 biometrics or primary auth can be used to unlock the device. */
+    val strongerAuthRequiredAfterNonStrongBiometricsTimeout =
+        containsFlag(
+            flag,
+            LockPatternUtils.StrongAuthTracker
+                .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT
+        )
+}
+
+private fun containsFlag(haystack: Int, needle: Int): Boolean {
+    return haystack and needle != 0
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
index c8bd958..9e7dec4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -38,7 +38,8 @@
 object FailedAuthenticationStatus : AuthenticationStatus()
 
 /** Face authentication error message */
-data class ErrorAuthenticationStatus(val msgId: Int, val msg: String?) : AuthenticationStatus() {
+data class ErrorAuthenticationStatus(val msgId: Int, val msg: String? = null) :
+    AuthenticationStatus() {
     /**
      * Method that checks if [msgId] is a lockout error. A lockout error means that face
      * authentication is locked out.
diff --git a/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt b/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt
new file mode 100644
index 0000000..3be4499
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
+import com.android.systemui.log.dagger.BouncerLog
+import javax.inject.Inject
+
+private const val TAG = "BouncerLog"
+
+/**
+ * Helper class for logging for classes in the [com.android.systemui.keyguard.bouncer] package.
+ *
+ * To enable logcat echoing for an entire buffer:
+ * ```
+ *   adb shell settings put global systemui/buffer/BouncerLog <logLevel>
+ *
+ * ```
+ */
+@SysUISingleton
+class BouncerLogger @Inject constructor(@BouncerLog private val buffer: LogBuffer) {
+    fun startBouncerMessageInteractor() {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            "Starting BouncerMessageInteractor.bouncerMessage collector"
+        )
+    }
+
+    fun bouncerMessageUpdated(bouncerMsg: BouncerMessageModel?) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                int1 = bouncerMsg?.message?.messageResId ?: -1
+                str1 = bouncerMsg?.message?.message
+                int2 = bouncerMsg?.secondaryMessage?.messageResId ?: -1
+                str2 = bouncerMsg?.secondaryMessage?.message
+            },
+            { "Bouncer message update received: $int1, $str1, $int2, $str2" }
+        )
+    }
+
+    fun bindingBouncerMessageView() {
+        buffer.log(TAG, LogLevel.DEBUG, "Binding BouncerMessageView")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerLog.kt
index 2251a7b..0c2e731 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerLog.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,10 +16,7 @@
 
 package com.android.systemui.log.dagger
 
-import java.lang.annotation.Documented
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
 import javax.inject.Qualifier
 
-/** Logger for the primary and alternative bouncers. */
-@Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) annotation class BouncerLog
+/** A [com.android.systemui.log.LogBuffer] for bouncer and its child views. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class BouncerLog()
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerTableLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerTableLog.kt
new file mode 100644
index 0000000..08df7db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerTableLog.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.systemui.log.dagger
+
+import java.lang.annotation.Documented
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+import javax.inject.Qualifier
+
+/** Logger for the primary and alternative bouncers. */
+@Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) annotation class BouncerTableLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/CarrierTextManagerLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/CarrierTextManagerLog.kt
new file mode 100644
index 0000000..62b80b2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/CarrierTextManagerLog.kt
@@ -0,0 +1,9 @@
+package com.android.systemui.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [LogBuffer] for detailed carrier text logs. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class CarrierTextManagerLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 408628f..0261ee5 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -373,6 +373,16 @@
     }
 
     /**
+     * Provides a {@link LogBuffer} for use by {@link com.android.keyguard.KeyguardUpdateMonitor}.
+     */
+    @Provides
+    @SysUISingleton
+    @CarrierTextManagerLog
+    public static LogBuffer provideCarrierTextManagerLog(LogBufferFactory factory) {
+        return factory.create("CarrierTextManagerLog", 100);
+    }
+
+    /**
      * Provides a {@link LogBuffer} for use by {@link com.android.systemui.ScreenDecorations}.
      */
     @Provides
@@ -394,6 +404,17 @@
     }
 
     /**
+     * Provides a {@link LogBuffer} for use by classes in the
+     *  {@link com.android.systemui.keyguard.bouncer} package.
+     */
+    @Provides
+    @SysUISingleton
+    @BouncerLog
+    public static LogBuffer provideBouncerLog(LogBufferFactory factory) {
+        return factory.create("BouncerLog", 100);
+    }
+
+    /**
      * Provides a {@link LogBuffer} for Device State Auto-Rotation logs.
      */
     @Provides
@@ -416,9 +437,9 @@
     /** Provides a logging buffer for the primary bouncer. */
     @Provides
     @SysUISingleton
-    @BouncerLog
+    @BouncerTableLog
     public static TableLogBuffer provideBouncerLogBuffer(TableLogBufferFactory factory) {
-        return factory.create("BouncerLog", 250);
+        return factory.create("BouncerTableLog", 250);
     }
 
     /** Provides a table logging buffer for the Monitor. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 8f0ac28..9eda7ae2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -1235,6 +1235,8 @@
         if ((buttonId == R.id.actionPrev && semanticActions.getReservePrev())
                 || (buttonId == R.id.actionNext && semanticActions.getReserveNext())) {
             notVisibleValue = ConstraintSet.INVISIBLE;
+            mMediaViewHolder.getAction(buttonId).setFocusable(visible);
+            mMediaViewHolder.getAction(buttonId).setClickable(visible);
         } else {
             notVisibleValue = ConstraintSet.GONE;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 1627662..316c903e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -72,102 +72,67 @@
     public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
             int viewType) {
         super.onCreateViewHolder(viewGroup, viewType);
-        if (mController.isAdvancedLayoutSupported()) {
-            switch (viewType) {
-                case MediaItem.MediaItemType.TYPE_GROUP_DIVIDER:
-                    return new MediaGroupDividerViewHolder(mHolderView);
-                case MediaItem.MediaItemType.TYPE_PAIR_NEW_DEVICE:
-                case MediaItem.MediaItemType.TYPE_DEVICE:
-                default:
-                    return new MediaDeviceViewHolder(mHolderView);
-            }
-        } else {
-            return new MediaDeviceViewHolder(mHolderView);
+        switch (viewType) {
+            case MediaItem.MediaItemType.TYPE_GROUP_DIVIDER:
+                return new MediaGroupDividerViewHolder(mHolderView);
+            case MediaItem.MediaItemType.TYPE_PAIR_NEW_DEVICE:
+            case MediaItem.MediaItemType.TYPE_DEVICE:
+            default:
+                return new MediaDeviceViewHolder(mHolderView);
         }
     }
 
     @Override
     public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
-        if (mController.isAdvancedLayoutSupported()) {
-            if (position >= mMediaItemList.size()) {
-                if (DEBUG) {
-                    Log.d(TAG, "Incorrect position: " + position + " list size: "
-                            + mMediaItemList.size());
-                }
-                return;
+        if (position >= mMediaItemList.size()) {
+            if (DEBUG) {
+                Log.d(TAG, "Incorrect position: " + position + " list size: "
+                        + mMediaItemList.size());
             }
-            MediaItem currentMediaItem = mMediaItemList.get(position);
-            switch (currentMediaItem.getMediaItemType()) {
-                case MediaItem.MediaItemType.TYPE_GROUP_DIVIDER:
-                    ((MediaGroupDividerViewHolder) viewHolder).onBind(currentMediaItem.getTitle());
-                    break;
-                case MediaItem.MediaItemType.TYPE_PAIR_NEW_DEVICE:
-                    ((MediaDeviceViewHolder) viewHolder).onBind(CUSTOMIZED_ITEM_PAIR_NEW);
-                    break;
-                case MediaItem.MediaItemType.TYPE_DEVICE:
-                    ((MediaDeviceViewHolder) viewHolder).onBind(
-                            currentMediaItem.getMediaDevice().get(),
-                            position);
-                    break;
-                default:
-                    Log.d(TAG, "Incorrect position: " + position);
-            }
-        } else {
-            final int size = mController.getMediaDevices().size();
-            if (position == size) {
+            return;
+        }
+        MediaItem currentMediaItem = mMediaItemList.get(position);
+        switch (currentMediaItem.getMediaItemType()) {
+            case MediaItem.MediaItemType.TYPE_GROUP_DIVIDER:
+                ((MediaGroupDividerViewHolder) viewHolder).onBind(currentMediaItem.getTitle());
+                break;
+            case MediaItem.MediaItemType.TYPE_PAIR_NEW_DEVICE:
                 ((MediaDeviceViewHolder) viewHolder).onBind(CUSTOMIZED_ITEM_PAIR_NEW);
-            } else if (position < size) {
+                break;
+            case MediaItem.MediaItemType.TYPE_DEVICE:
                 ((MediaDeviceViewHolder) viewHolder).onBind(
-                        ((List<MediaDevice>) (mController.getMediaDevices())).get(position),
+                        currentMediaItem.getMediaDevice().get(),
                         position);
-            } else if (DEBUG) {
+                break;
+            default:
                 Log.d(TAG, "Incorrect position: " + position);
-            }
         }
     }
 
     @Override
     public long getItemId(int position) {
-        if (mController.isAdvancedLayoutSupported()) {
-            if (position >= mMediaItemList.size()) {
-                Log.d(TAG, "Incorrect position for item id: " + position);
-                return position;
-            }
-            MediaItem currentMediaItem = mMediaItemList.get(position);
-            return currentMediaItem.getMediaDevice().isPresent()
-                    ? currentMediaItem.getMediaDevice().get().getId().hashCode()
-                    : position;
-        }
-        final int size = mController.getMediaDevices().size();
-        if (position == size) {
-            return -1;
-        } else if (position < size) {
-            return ((List<MediaDevice>) (mController.getMediaDevices()))
-                    .get(position).getId().hashCode();
-        } else if (DEBUG) {
+        if (position >= mMediaItemList.size()) {
             Log.d(TAG, "Incorrect position for item id: " + position);
+            return position;
         }
-        return position;
+        MediaItem currentMediaItem = mMediaItemList.get(position);
+        return currentMediaItem.getMediaDevice().isPresent()
+                ? currentMediaItem.getMediaDevice().get().getId().hashCode()
+                : position;
     }
 
     @Override
     public int getItemViewType(int position) {
-        if (mController.isAdvancedLayoutSupported()
-                && position >= mMediaItemList.size()) {
+        if (position >= mMediaItemList.size()) {
             Log.d(TAG, "Incorrect position for item type: " + position);
             return MediaItem.MediaItemType.TYPE_GROUP_DIVIDER;
         }
-        return mController.isAdvancedLayoutSupported()
-                ? mMediaItemList.get(position).getMediaItemType()
-                : super.getItemViewType(position);
+        return mMediaItemList.get(position).getMediaItemType();
     }
 
     @Override
     public int getItemCount() {
-        // Add extra one for "pair new"
-        return mController.isAdvancedLayoutSupported()
-                ? mMediaItemList.size()
-                : mController.getMediaDevices().size() + 1;
+        return mMediaItemList.size();
     }
 
     class MediaDeviceViewHolder extends MediaDeviceBaseViewHolder {
@@ -203,17 +168,14 @@
                 // Set different layout for each device
                 if (device.isMutingExpectedDevice()
                         && !mController.isCurrentConnectedDeviceRemote()) {
-                    if (!mController.isAdvancedLayoutSupported()) {
-                        updateTitleIcon(R.drawable.media_output_icon_volume,
-                                mController.getColorItemContent());
-                    }
+                    updateTitleIcon(R.drawable.media_output_icon_volume,
+                            mController.getColorItemContent());
                     mCurrentActivePosition = position;
                     updateFullItemClickListener(v -> onItemClick(v, device));
                     setSingleLineLayout(getItemTitle(device));
                     initMutingExpectedDevice();
                 } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
-                        && mController.isSubStatusSupported()
-                        && mController.isAdvancedLayoutSupported() && device.hasSubtext()) {
+                        && device.hasSubtext()) {
                     boolean isActiveWithOngoingSession =
                             (device.hasOngoingSession() && (currentlyConnected || isDeviceIncluded(
                                     mController.getSelectedMediaDevice(), device)));
@@ -284,10 +246,8 @@
                     // selected device in group
                     boolean isDeviceDeselectable = isDeviceIncluded(
                             mController.getDeselectableMediaDevice(), device);
-                    if (!mController.isAdvancedLayoutSupported()) {
-                        updateTitleIcon(R.drawable.media_output_icon_volume,
-                                mController.getColorItemContent());
-                    }
+                    updateTitleIcon(R.drawable.media_output_icon_volume,
+                            mController.getColorItemContent());
                     updateGroupableCheckBox(true, isDeviceDeselectable, device);
                     updateEndClickArea(device, isDeviceDeselectable);
                     setUpContentDescriptionForView(mContainerLayout, false, device);
@@ -305,8 +265,7 @@
                         updateFullItemClickListener(v -> cancelMuteAwaitConnection());
                         setSingleLineLayout(getItemTitle(device));
                     } else if (mController.isCurrentConnectedDeviceRemote()
-                            && !mController.getSelectableMediaDevice().isEmpty()
-                            && mController.isAdvancedLayoutSupported()) {
+                            && !mController.getSelectableMediaDevice().isEmpty()) {
                         //If device is connected and there's other selectable devices, layout as
                         // one of selected devices.
                         updateTitleIcon(R.drawable.media_output_icon_volume,
@@ -334,36 +293,27 @@
                     //groupable device
                     setUpDeviceIcon(device);
                     updateGroupableCheckBox(false, true, device);
-                    if (mController.isAdvancedLayoutSupported()) {
-                        updateEndClickArea(device, true);
-                    }
-                    updateFullItemClickListener(mController.isAdvancedLayoutSupported()
-                            ? v -> onItemClick(v, device)
-                            : v -> onGroupActionTriggered(true, device));
+                    updateEndClickArea(device, true);
+                    updateFullItemClickListener(v -> onItemClick(v, device));
                     setSingleLineLayout(getItemTitle(device), false /* showSeekBar */,
                             false /* showProgressBar */, true /* showCheckBox */,
                             true /* showEndTouchArea */);
                 } else {
                     setUpDeviceIcon(device);
                     setSingleLineLayout(getItemTitle(device));
-                    if (mController.isAdvancedLayoutSupported()
-                            && mController.isSubStatusSupported()) {
-                        Drawable deviceStatusIcon =
-                                device.hasOngoingSession() ? mContext.getDrawable(
-                                        R.drawable.ic_sound_bars_anim)
-                                        : Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior(
-                                                device,
-                                                mContext);
-                        if (deviceStatusIcon != null) {
-                            updateDeviceStatusIcon(deviceStatusIcon);
-                            mStatusIcon.setVisibility(View.VISIBLE);
-                        }
-                        updateSingleLineLayoutContentAlpha(
-                                updateClickActionBasedOnSelectionBehavior(device)
-                                        ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
-                    } else {
-                        updateFullItemClickListener(v -> onItemClick(v, device));
+                    Drawable deviceStatusIcon =
+                            device.hasOngoingSession() ? mContext.getDrawable(
+                                    R.drawable.ic_sound_bars_anim)
+                                    : Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior(
+                                            device,
+                                            mContext);
+                    if (deviceStatusIcon != null) {
+                        updateDeviceStatusIcon(deviceStatusIcon);
+                        mStatusIcon.setVisibility(View.VISIBLE);
                     }
+                    updateSingleLineLayoutContentAlpha(
+                            updateClickActionBasedOnSelectionBehavior(device)
+                                    ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
                 }
             }
         }
@@ -400,10 +350,8 @@
         }
 
         public void updateEndClickAreaColor(int color) {
-            if (mController.isAdvancedLayoutSupported()) {
-                mEndTouchArea.setBackgroundTintList(
-                        ColorStateList.valueOf(color));
-            }
+            mEndTouchArea.setBackgroundTintList(
+                    ColorStateList.valueOf(color));
         }
 
         private boolean updateClickActionBasedOnSelectionBehavior(MediaDevice device) {
@@ -440,10 +388,8 @@
                     isDeviceDeselectable ? (v) -> mCheckBox.performClick() : null);
             mEndTouchArea.setImportantForAccessibility(
                     View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-            if (mController.isAdvancedLayoutSupported()) {
-                mEndTouchArea.setBackgroundTintList(
-                        ColorStateList.valueOf(mController.getColorItemBackground()));
-            }
+            mEndTouchArea.setBackgroundTintList(
+                    ColorStateList.valueOf(mController.getColorItemBackground()));
             setUpContentDescriptionForView(mEndTouchArea, true, device);
         }
 
@@ -473,10 +419,8 @@
                 mTitleIcon.setImageDrawable(addDrawable);
                 mTitleIcon.setImageTintList(
                         ColorStateList.valueOf(mController.getColorItemContent()));
-                if (mController.isAdvancedLayoutSupported()) {
-                    mIconAreaLayout.setBackgroundTintList(
-                            ColorStateList.valueOf(mController.getColorItemBackground()));
-                }
+                mIconAreaLayout.setBackgroundTintList(
+                        ColorStateList.valueOf(mController.getColorItemBackground()));
                 mContainerLayout.setOnClickListener(mController::launchBluetoothPairing);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 6ebda40..af06258 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -89,9 +89,8 @@
     public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
             int viewType) {
         mContext = viewGroup.getContext();
-        mHolderView = LayoutInflater.from(mContext).inflate(
-                mController.isAdvancedLayoutSupported() ? MediaItem.getMediaLayoutId(viewType)
-                        : R.layout.media_output_list_item, viewGroup, false);
+        mHolderView = LayoutInflater.from(mContext).inflate(MediaItem.getMediaLayoutId(viewType),
+                viewGroup, false);
 
         return null;
     }
@@ -173,15 +172,9 @@
             mStatusIcon = view.requireViewById(R.id.media_output_item_status);
             mCheckBox = view.requireViewById(R.id.check_box);
             mEndTouchArea = view.requireViewById(R.id.end_action_area);
-            if (mController.isAdvancedLayoutSupported()) {
-                mEndClickIcon = view.requireViewById(R.id.media_output_item_end_click_icon);
-                mVolumeValueText = view.requireViewById(R.id.volume_value);
-                mIconAreaLayout = view.requireViewById(R.id.icon_area);
-            } else {
-                mVolumeValueText = null;
-                mIconAreaLayout = null;
-                mEndClickIcon = null;
-            }
+            mEndClickIcon = view.requireViewById(R.id.media_output_item_end_click_icon);
+            mVolumeValueText = view.requireViewById(R.id.volume_value);
+            mIconAreaLayout = view.requireViewById(R.id.icon_area);
             initAnimator();
         }
 
@@ -197,9 +190,7 @@
             mTitleText.setTextColor(mController.getColorItemContent());
             mSubTitleText.setTextColor(mController.getColorItemContent());
             mTwoLineTitleText.setTextColor(mController.getColorItemContent());
-            if (mController.isAdvancedLayoutSupported()) {
-                mVolumeValueText.setTextColor(mController.getColorItemContent());
-            }
+            mVolumeValueText.setTextColor(mController.getColorItemContent());
             mSeekBar.setProgressTintList(
                     ColorStateList.valueOf(mController.getColorSeekbarProgress()));
         }
@@ -230,12 +221,10 @@
             mItemLayout.setBackgroundTintList(
                     ColorStateList.valueOf(isActive ? mController.getColorConnectedItemBackground()
                             : mController.getColorItemBackground()));
-            if (mController.isAdvancedLayoutSupported()) {
-                mIconAreaLayout.setBackgroundTintList(
-                        ColorStateList.valueOf(showSeekBar ? mController.getColorSeekbarProgress()
-                                : showProgressBar ? mController.getColorConnectedItemBackground()
-                                        : mController.getColorItemBackground()));
-            }
+            mIconAreaLayout.setBackgroundTintList(
+                    ColorStateList.valueOf(showSeekBar ? mController.getColorSeekbarProgress()
+                            : showProgressBar ? mController.getColorConnectedItemBackground()
+                                    : mController.getColorItemBackground()));
             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
             mSeekBar.setAlpha(1);
             mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
@@ -246,12 +235,10 @@
             mTitleText.setVisibility(View.VISIBLE);
             mCheckBox.setVisibility(showCheckBox ? View.VISIBLE : View.GONE);
             mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
-            if (mController.isAdvancedLayoutSupported()) {
-                ViewGroup.MarginLayoutParams params =
-                        (ViewGroup.MarginLayoutParams) mItemLayout.getLayoutParams();
-                params.rightMargin = showEndTouchArea ? mController.getItemMarginEndSelectable()
-                        : mController.getItemMarginEndDefault();
-            }
+            ViewGroup.MarginLayoutParams params =
+                    (ViewGroup.MarginLayoutParams) mItemLayout.getLayoutParams();
+            params.rightMargin = showEndTouchArea ? mController.getItemMarginEndSelectable()
+                    : mController.getItemMarginEndDefault();
             mTitleIcon.setBackgroundTintList(
                     ColorStateList.valueOf(mController.getColorItemContent()));
         }
@@ -272,36 +259,28 @@
             mSeekBar.setAlpha(1);
             mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
             final Drawable backgroundDrawable;
-            if (mController.isAdvancedLayoutSupported() && mController.isSubStatusSupported()) {
-                backgroundDrawable = mContext.getDrawable(
-                        showSeekBar || isFakeActive ? R.drawable.media_output_item_background_active
-                                : R.drawable.media_output_item_background).mutate();
-                mItemLayout.setBackgroundTintList(ColorStateList.valueOf(
-                        showSeekBar || isFakeActive ? mController.getColorConnectedItemBackground()
-                                : mController.getColorItemBackground()
-                ));
-                mIconAreaLayout.setBackgroundTintList(
-                        ColorStateList.valueOf(showProgressBar || isFakeActive
-                                ? mController.getColorConnectedItemBackground()
-                                : showSeekBar ? mController.getColorSeekbarProgress()
-                                        : mController.getColorItemBackground()));
-                if (showSeekBar) {
-                    updateSeekbarProgressBackground();
-                }
-                //update end click area by isActive
-                mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
-                mEndClickIcon.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
-                ViewGroup.MarginLayoutParams params =
-                        (ViewGroup.MarginLayoutParams) mItemLayout.getLayoutParams();
-                params.rightMargin = showEndTouchArea ? mController.getItemMarginEndSelectable()
-                        : mController.getItemMarginEndDefault();
-            } else {
-                backgroundDrawable = mContext.getDrawable(
-                                R.drawable.media_output_item_background)
-                        .mutate();
-                mItemLayout.setBackgroundTintList(
-                        ColorStateList.valueOf(mController.getColorItemBackground()));
+            backgroundDrawable = mContext.getDrawable(
+                    showSeekBar || isFakeActive ? R.drawable.media_output_item_background_active
+                            : R.drawable.media_output_item_background).mutate();
+            mItemLayout.setBackgroundTintList(ColorStateList.valueOf(
+                    showSeekBar || isFakeActive ? mController.getColorConnectedItemBackground()
+                            : mController.getColorItemBackground()
+            ));
+            mIconAreaLayout.setBackgroundTintList(
+                    ColorStateList.valueOf(showProgressBar || isFakeActive
+                            ? mController.getColorConnectedItemBackground()
+                            : showSeekBar ? mController.getColorSeekbarProgress()
+                                    : mController.getColorItemBackground()));
+            if (showSeekBar) {
+                updateSeekbarProgressBackground();
             }
+            //update end click area by isActive
+            mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
+            mEndClickIcon.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
+            ViewGroup.MarginLayoutParams params =
+                    (ViewGroup.MarginLayoutParams) mItemLayout.getLayoutParams();
+            params.rightMargin = showEndTouchArea ? mController.getItemMarginEndSelectable()
+                    : mController.getItemMarginEndDefault();
             mItemLayout.setBackground(backgroundDrawable);
             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
             mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
@@ -319,15 +298,11 @@
                             .findDrawableByLayerId(android.R.id.progress);
             final GradientDrawable progressDrawable =
                     (GradientDrawable) clipDrawable.getDrawable();
-            if (mController.isAdvancedLayoutSupported()) {
-                progressDrawable.setCornerRadii(
-                        new float[]{0, 0, mController.getActiveRadius(),
-                                mController.getActiveRadius(),
-                                mController.getActiveRadius(),
-                                mController.getActiveRadius(), 0, 0});
-            } else {
-                progressDrawable.setCornerRadius(mController.getActiveRadius());
-            }
+            progressDrawable.setCornerRadii(
+                    new float[]{0, 0, mController.getActiveRadius(),
+                            mController.getActiveRadius(),
+                            mController.getActiveRadius(),
+                            mController.getActiveRadius(), 0, 0});
         }
 
         void initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible) {
@@ -340,30 +315,23 @@
             final int currentVolume = device.getCurrentVolume();
             if (mSeekBar.getVolume() != currentVolume) {
                 if (isCurrentSeekbarInvisible && !mIsInitVolumeFirstTime) {
-                    if (mController.isAdvancedLayoutSupported()) {
-                        updateTitleIcon(currentVolume == 0 ? R.drawable.media_output_icon_volume_off
-                                        : R.drawable.media_output_icon_volume,
-                                mController.getColorItemContent());
-                    } else {
-                        animateCornerAndVolume(mSeekBar.getProgress(),
-                                MediaOutputSeekbar.scaleVolumeToProgress(currentVolume));
-                    }
+                    updateTitleIcon(currentVolume == 0 ? R.drawable.media_output_icon_volume_off
+                                    : R.drawable.media_output_icon_volume,
+                            mController.getColorItemContent());
                 } else {
                     if (!mVolumeAnimator.isStarted()) {
-                        if (mController.isAdvancedLayoutSupported()) {
-                            int percentage =
-                                    (int) ((double) currentVolume * VOLUME_PERCENTAGE_SCALE_SIZE
-                                            / (double) mSeekBar.getMax());
-                            if (percentage == 0) {
-                                updateMutedVolumeIcon();
-                            } else {
-                                updateUnmutedVolumeIcon();
-                            }
+                        int percentage =
+                                (int) ((double) currentVolume * VOLUME_PERCENTAGE_SCALE_SIZE
+                                        / (double) mSeekBar.getMax());
+                        if (percentage == 0) {
+                            updateMutedVolumeIcon();
+                        } else {
+                            updateUnmutedVolumeIcon();
                         }
                         mSeekBar.setVolume(currentVolume);
                     }
                 }
-            } else if (mController.isAdvancedLayoutSupported() && currentVolume == 0) {
+            } else if (currentVolume == 0) {
                 mSeekBar.resetVolume();
                 updateMutedVolumeIcon();
             }
@@ -378,17 +346,15 @@
                     }
                     int progressToVolume = MediaOutputSeekbar.scaleProgressToVolume(progress);
                     int deviceVolume = device.getCurrentVolume();
-                    if (mController.isAdvancedLayoutSupported()) {
-                        int percentage =
-                                (int) ((double) progressToVolume * VOLUME_PERCENTAGE_SCALE_SIZE
-                                        / (double) seekBar.getMax());
-                        mVolumeValueText.setText(mContext.getResources().getString(
-                                R.string.media_output_dialog_volume_percentage, percentage));
-                        mVolumeValueText.setVisibility(View.VISIBLE);
-                    }
+                    int percentage =
+                            (int) ((double) progressToVolume * VOLUME_PERCENTAGE_SCALE_SIZE
+                                    / (double) seekBar.getMax());
+                    mVolumeValueText.setText(mContext.getResources().getString(
+                            R.string.media_output_dialog_volume_percentage, percentage));
+                    mVolumeValueText.setVisibility(View.VISIBLE);
                     if (progressToVolume != deviceVolume) {
                         mController.adjustVolume(device, progressToVolume);
-                        if (mController.isAdvancedLayoutSupported() && deviceVolume == 0) {
+                        if (deviceVolume == 0) {
                             updateUnmutedVolumeIcon();
                         }
                     }
@@ -396,30 +362,26 @@
 
                 @Override
                 public void onStartTrackingTouch(SeekBar seekBar) {
-                    if (mController.isAdvancedLayoutSupported()) {
-                        mTitleIcon.setVisibility(View.INVISIBLE);
-                        mVolumeValueText.setVisibility(View.VISIBLE);
-                    }
+                    mTitleIcon.setVisibility(View.INVISIBLE);
+                    mVolumeValueText.setVisibility(View.VISIBLE);
                     mIsDragging = true;
                 }
 
                 @Override
                 public void onStopTrackingTouch(SeekBar seekBar) {
-                    if (mController.isAdvancedLayoutSupported()) {
-                        int currentVolume = MediaOutputSeekbar.scaleProgressToVolume(
-                                seekBar.getProgress());
-                        int percentage =
-                                (int) ((double) currentVolume * VOLUME_PERCENTAGE_SCALE_SIZE
-                                        / (double) seekBar.getMax());
-                        if (percentage == 0) {
-                            seekBar.setProgress(0);
-                            updateMutedVolumeIcon();
-                        } else {
-                            updateUnmutedVolumeIcon();
-                        }
-                        mTitleIcon.setVisibility(View.VISIBLE);
-                        mVolumeValueText.setVisibility(View.GONE);
+                    int currentVolume = MediaOutputSeekbar.scaleProgressToVolume(
+                            seekBar.getProgress());
+                    int percentage =
+                            (int) ((double) currentVolume * VOLUME_PERCENTAGE_SCALE_SIZE
+                                    / (double) seekBar.getMax());
+                    if (percentage == 0) {
+                        seekBar.setProgress(0);
+                        updateMutedVolumeIcon();
+                    } else {
+                        updateUnmutedVolumeIcon();
                     }
+                    mTitleIcon.setVisibility(View.VISIBLE);
+                    mVolumeValueText.setVisibility(View.GONE);
                     mController.logInteractionAdjustVolume(device);
                     mIsDragging = false;
                 }
@@ -444,10 +406,8 @@
         void updateTitleIcon(@DrawableRes int id, int color) {
             mTitleIcon.setImageDrawable(mContext.getDrawable(id));
             mTitleIcon.setImageTintList(ColorStateList.valueOf(color));
-            if (mController.isAdvancedLayoutSupported()) {
-                mIconAreaLayout.setBackgroundTintList(
-                        ColorStateList.valueOf(mController.getColorSeekbarProgress()));
-            }
+            mIconAreaLayout.setBackgroundTintList(
+                    ColorStateList.valueOf(mController.getColorSeekbarProgress()));
         }
 
         void updateIconAreaClickListener(View.OnClickListener listener) {
@@ -475,24 +435,18 @@
                     (ClipDrawable) ((LayerDrawable) mSeekBar.getProgressDrawable())
                             .findDrawableByLayerId(android.R.id.progress);
             final GradientDrawable targetBackgroundDrawable =
-                    (GradientDrawable) (mController.isAdvancedLayoutSupported()
-                            ? mIconAreaLayout.getBackground()
-                            : clipDrawable.getDrawable());
+                    (GradientDrawable) (mIconAreaLayout.getBackground());
             mCornerAnimator.addUpdateListener(animation -> {
                 float value = (float) animation.getAnimatedValue();
                 layoutBackgroundDrawable.setCornerRadius(value);
-                if (mController.isAdvancedLayoutSupported()) {
-                    if (toProgress == 0) {
-                        targetBackgroundDrawable.setCornerRadius(value);
-                    } else {
-                        targetBackgroundDrawable.setCornerRadii(new float[]{
-                                value,
-                                value,
-                                0, 0, 0, 0, value, value
-                        });
-                    }
-                } else {
+                if (toProgress == 0) {
                     targetBackgroundDrawable.setCornerRadius(value);
+                } else {
+                    targetBackgroundDrawable.setCornerRadii(new float[]{
+                            value,
+                            value,
+                            0, 0, 0, 0, value, value
+                    });
                 }
             });
             mVolumeAnimator.setIntValues(fromProgress, toProgress);
@@ -539,9 +493,7 @@
         protected void disableSeekBar() {
             mSeekBar.setEnabled(false);
             mSeekBar.setOnTouchListener((v, event) -> true);
-            if (mController.isAdvancedLayoutSupported()) {
-                updateIconAreaClickListener(null);
-            }
+            updateIconAreaClickListener(null);
         }
 
         private void enableSeekBar(MediaDevice device) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 08b799a..0a5b4b3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -270,10 +270,8 @@
             dismiss();
         });
         mAppButton.setOnClickListener(mMediaOutputController::tryToLaunchMediaApplication);
-        if (mMediaOutputController.isAdvancedLayoutSupported()) {
-            mMediaMetadataSectionLayout.setOnClickListener(
-                    mMediaOutputController::tryToLaunchMediaApplication);
-        }
+        mMediaMetadataSectionLayout.setOnClickListener(
+                mMediaOutputController::tryToLaunchMediaApplication);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 2713642..cc75478 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -83,7 +83,6 @@
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.monet.ColorScheme;
 import com.android.systemui.plugins.ActivityStarter;
@@ -132,8 +131,6 @@
     private final Object mMediaDevicesLock = new Object();
     @VisibleForTesting
     final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
-    @VisibleForTesting
-    final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
     final List<MediaDevice> mCachedMediaDevices = new CopyOnWriteArrayList<>();
     private final List<MediaItem> mMediaItemList = new CopyOnWriteArrayList<>();
     private final AudioManager mAudioManager;
@@ -229,7 +226,6 @@
     void start(@NonNull Callback cb) {
         synchronized (mMediaDevicesLock) {
             mCachedMediaDevices.clear();
-            mMediaDevices.clear();
             mMediaItemList.clear();
         }
         mNearbyDeviceInfoMap.clear();
@@ -277,7 +273,6 @@
         mLocalMediaManager.stopScan();
         synchronized (mMediaDevicesLock) {
             mCachedMediaDevices.clear();
-            mMediaDevices.clear();
             mMediaItemList.clear();
         }
         if (mNearbyMediaDevicesManager != null) {
@@ -308,10 +303,9 @@
 
     @Override
     public void onDeviceListUpdate(List<MediaDevice> devices) {
-        boolean isListEmpty =
-                isAdvancedLayoutSupported() ? mMediaItemList.isEmpty() : mMediaDevices.isEmpty();
+        boolean isListEmpty = mMediaItemList.isEmpty();
         if (isListEmpty || !mIsRefreshing) {
-            buildMediaDevices(devices);
+            buildMediaItems(devices);
             mCallback.onDeviceListChanged();
         } else {
             synchronized (mMediaDevicesLock) {
@@ -326,11 +320,7 @@
     public void onSelectedDeviceStateChanged(MediaDevice device,
             @LocalMediaManager.MediaDeviceState int state) {
         mCallback.onRouteChanged();
-        if (isAdvancedLayoutSupported()) {
-            mMetricLogger.logOutputItemSuccess(device.toString(), new ArrayList<>(mMediaItemList));
-        } else {
-            mMetricLogger.logOutputSuccess(device.toString(), new ArrayList<>(mMediaDevices));
-        }
+        mMetricLogger.logOutputItemSuccess(device.toString(), new ArrayList<>(mMediaItemList));
     }
 
     @Override
@@ -341,11 +331,7 @@
     @Override
     public void onRequestFailed(int reason) {
         mCallback.onRouteChanged();
-        if (isAdvancedLayoutSupported()) {
-            mMetricLogger.logOutputItemFailure(new ArrayList<>(mMediaItemList), reason);
-        } else {
-            mMetricLogger.logOutputFailure(new ArrayList<>(mMediaDevices), reason);
-        }
+        mMetricLogger.logOutputItemFailure(new ArrayList<>(mMediaItemList), reason);
     }
 
     /**
@@ -364,7 +350,6 @@
         }
         try {
             synchronized (mMediaDevicesLock) {
-                mMediaDevices.removeIf(MediaDevice::isMutingExpectedDevice);
                 mMediaItemList.removeIf((MediaItem::isMutingExpectedDevice));
             }
             mAudioManager.cancelMuteAwaitConnection(mAudioManager.getMutingExpectedDevice());
@@ -569,7 +554,7 @@
 
     void refreshDataSetIfNeeded() {
         if (mNeedRefresh) {
-            buildMediaDevices(mCachedMediaDevices);
+            buildMediaItems(mCachedMediaDevices);
             mCallback.onDeviceListChanged();
             mNeedRefresh = false;
         }
@@ -619,75 +604,9 @@
         return mItemMarginEndSelectable;
     }
 
-    private void buildMediaDevices(List<MediaDevice> devices) {
-        if (isAdvancedLayoutSupported()) {
-            buildMediaItems(devices);
-        } else {
-            buildDefaultMediaDevices(devices);
-        }
-    }
-
-    private void buildDefaultMediaDevices(List<MediaDevice> devices) {
-        synchronized (mMediaDevicesLock) {
-            attachRangeInfo(devices);
-            Collections.sort(devices, Comparator.naturalOrder());
-            // For the first time building list, to make sure the top device is the connected
-            // device.
-            if (mMediaDevices.isEmpty()) {
-                boolean needToHandleMutingExpectedDevice =
-                        hasMutingExpectedDevice() && !isCurrentConnectedDeviceRemote();
-                final MediaDevice connectedMediaDevice =
-                        needToHandleMutingExpectedDevice ? null
-                                : getCurrentConnectedMediaDevice();
-                if (connectedMediaDevice == null) {
-                    if (DEBUG) {
-                        Log.d(TAG, "No connected media device or muting expected device exist.");
-                    }
-                    if (needToHandleMutingExpectedDevice) {
-                        for (MediaDevice device : devices) {
-                            if (device.isMutingExpectedDevice()) {
-                                mMediaDevices.add(0, device);
-                            } else {
-                                mMediaDevices.add(device);
-                            }
-                        }
-                    } else {
-                        mMediaDevices.addAll(devices);
-                    }
-                    return;
-                }
-                for (MediaDevice device : devices) {
-                    if (TextUtils.equals(device.getId(), connectedMediaDevice.getId())) {
-                        mMediaDevices.add(0, device);
-                    } else {
-                        mMediaDevices.add(device);
-                    }
-                }
-                return;
-            }
-            // To keep the same list order
-            final List<MediaDevice> targetMediaDevices = new ArrayList<>();
-            for (MediaDevice originalDevice : mMediaDevices) {
-                for (MediaDevice newDevice : devices) {
-                    if (TextUtils.equals(originalDevice.getId(), newDevice.getId())) {
-                        targetMediaDevices.add(newDevice);
-                        break;
-                    }
-                }
-            }
-            if (targetMediaDevices.size() != devices.size()) {
-                devices.removeAll(targetMediaDevices);
-                targetMediaDevices.addAll(devices);
-            }
-            mMediaDevices.clear();
-            mMediaDevices.addAll(targetMediaDevices);
-        }
-    }
-
     private void buildMediaItems(List<MediaDevice> devices) {
         synchronized (mMediaDevicesLock) {
-            if (!isRouteProcessSupported() || (isRouteProcessSupported()
-                    && !mLocalMediaManager.isPreferenceRouteListingExist())) {
+            if (!mLocalMediaManager.isPreferenceRouteListingExist()) {
                 attachRangeInfo(devices);
                 Collections.sort(devices, Comparator.naturalOrder());
             }
@@ -811,18 +730,6 @@
                 && (currentConnectedMediaDevice.isHostForOngoingSession());
     }
 
-    public boolean isAdvancedLayoutSupported() {
-        return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT);
-    }
-
-    public boolean isRouteProcessSupported() {
-        return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_ROUTES_PROCESSING);
-    }
-
-    public boolean isSubStatusSupported() {
-        return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_DEVICE_STATUS);
-    }
-
     List<MediaDevice> getGroupMediaDevices() {
         final List<MediaDevice> selectedDevices = getSelectedMediaDevice();
         final List<MediaDevice> selectableDevices = getSelectableMediaDevice();
@@ -867,10 +774,6 @@
         });
     }
 
-    Collection<MediaDevice> getMediaDevices() {
-        return mMediaDevices;
-    }
-
     public List<MediaItem> getMediaItemList() {
         return mMediaItemList;
     }
@@ -957,19 +860,11 @@
 
     boolean isAnyDeviceTransferring() {
         synchronized (mMediaDevicesLock) {
-            if (isAdvancedLayoutSupported()) {
-                for (MediaItem mediaItem : mMediaItemList) {
-                    if (mediaItem.getMediaDevice().isPresent()
-                            && mediaItem.getMediaDevice().get().getState()
-                            == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
-                        return true;
-                    }
-                }
-            } else {
-                for (MediaDevice device : mMediaDevices) {
-                    if (device.getState() == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
-                        return true;
-                    }
+            for (MediaItem mediaItem : mMediaItemList) {
+                if (mediaItem.getMediaDevice().isPresent()
+                        && mediaItem.getMediaDevice().get().getState()
+                        == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
+                    return true;
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 8af488e..417d4ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -321,7 +321,8 @@
     protected void setBackgroundTintColor(int color) {
         if (color != mCurrentBackgroundTint) {
             mCurrentBackgroundTint = color;
-            if (color == mNormalColor) {
+            // TODO(282173943): re-enable this tinting optimization when Resources are thread-safe
+            if (false && color == mNormalColor) {
                 // We don't need to tint a normal notification
                 color = 0;
             }
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 6bbeebf..0989df6 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
@@ -16,11 +16,15 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static android.graphics.PorterDuff.Mode.SRC_ATOP;
+
 import android.annotation.ColorInt;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.ColorFilter;
+import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.IndentingPrintWriter;
@@ -157,10 +161,20 @@
      */
     public void updateColors() {
         Resources.Theme theme = mContext.getTheme();
-        int textColor = getResources().getColor(R.color.notif_pill_text, theme);
-        mClearAllButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
+        final @ColorInt int textColor = getResources().getColor(R.color.notif_pill_text, theme);
+        final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
+        final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
+        // TODO(b/282173943): Remove redundant tinting once Resources are thread-safe
+        final @ColorInt int buttonBgColor =
+                Utils.getColorAttrDefaultColor(mContext, com.android.internal.R.attr.colorSurface);
+        final ColorFilter bgColorFilter = new PorterDuffColorFilter(buttonBgColor, SRC_ATOP);
+        if (buttonBgColor != 0) {
+            clearAllBg.setColorFilter(bgColorFilter);
+            manageBg.setColorFilter(bgColorFilter);
+        }
+        mClearAllButton.setBackground(clearAllBg);
         mClearAllButton.setTextColor(textColor);
-        mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
+        mManageButton.setBackground(manageBg);
         mManageButton.setTextColor(textColor);
         final @ColorInt int labelTextColor =
                 Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index eb20bba..991ff56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -21,6 +21,7 @@
 import com.android.settingslib.SignalIcon
 import com.android.settingslib.mobile.MobileMappings
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
@@ -62,6 +63,7 @@
  */
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
 class MobileRepositorySwitcher
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
index b129617..e96288a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
@@ -19,6 +19,7 @@
 import android.os.Bundle
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
@@ -54,6 +55,7 @@
  */
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
 class WifiRepositorySwitcher
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 4fbbc89..ab6409b 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -21,6 +21,8 @@
 import android.content.Context
 import android.graphics.Rect
 import android.os.PowerManager
+import android.os.Process
+import android.os.VibrationAttributes
 import android.view.Gravity
 import android.view.MotionEvent
 import android.view.View
@@ -226,7 +228,15 @@
         maybeGetAccessibilityFocus(newInfo, currentView)
 
         // ---- Haptics ----
-        newInfo.vibrationEffect?.let { vibratorHelper.vibrate(it) }
+        newInfo.vibrationEffect?.let {
+            vibratorHelper.vibrate(
+                Process.myUid(),
+                context.getApplicationContext().getPackageName(),
+                it,
+                newInfo.windowTitle,
+                VIBRATION_ATTRIBUTES,
+            )
+        }
     }
 
     private fun maybeGetAccessibilityFocus(info: ChipbarInfo?, view: ViewGroup) {
@@ -352,6 +362,11 @@
         val loadingView: View,
         val animator: ObjectAnimator,
     )
+
+    companion object {
+        val VIBRATION_ATTRIBUTES: VibrationAttributes =
+            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK)
+    }
 }
 
 @IdRes private val INFO_TAG = R.id.tag_chipbar_info
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
index 5557efa..48a8d1b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
@@ -21,6 +21,8 @@
 import static android.telephony.SubscriptionManager.DATA_ROAMING_ENABLE;
 import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.Assert.assertTrue;
@@ -30,13 +32,18 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.provider.Settings;
 import android.telephony.ServiceState;
@@ -47,8 +54,10 @@
 import android.testing.AndroidTestingRunner;
 import android.text.TextUtils;
 
+import com.android.keyguard.logging.CarrierTextManagerLogger;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.LogBufferHelperKt;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository;
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel;
@@ -110,6 +119,10 @@
 
     private CarrierTextManager mCarrierTextManager;
 
+    private CarrierTextManagerLogger mLogger =
+            new CarrierTextManagerLogger(
+                    LogBufferHelperKt.logcatLogBuffer("CarrierTextManagerLog"));
+
     private Void checkMainThread(InvocationOnMock inv) {
         assertThat(mMainExecutor.isExecuting()).isTrue();
         assertThat(mBgExecutor.isExecuting()).isFalse();
@@ -144,7 +157,7 @@
         mCarrierTextManager = new CarrierTextManager.Builder(
                 mContext, mContext.getResources(), mWifiRepository,
                 mTelephonyManager, mTelephonyListenerManager, mWakefulnessLifecycle, mMainExecutor,
-                mBgExecutor, mKeyguardUpdateMonitor)
+                mBgExecutor, mKeyguardUpdateMonitor, mLogger)
                 .setShowAirplaneMode(true)
                 .setShowMissingSim(true)
                 .build();
@@ -183,6 +196,47 @@
         assertEquals(AIRPLANE_MODE_TEXT, captor.getValue().carrierText);
     }
 
+    /** regression test for b/281706473, caused by sending NULL plmn / spn to the logger */
+    @Test
+    public void testAirplaneMode_noSim_nullPlmn_nullSpn_doesNotCrash() {
+        // GIVEN - sticy broadcast that returns a null PLMN and null SPN
+        Intent stickyIntent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+        stickyIntent.putExtra(TelephonyManager.EXTRA_SHOW_PLMN, true);
+        stickyIntent.removeExtra(TelephonyManager.EXTRA_PLMN);
+        stickyIntent.putExtra(TelephonyManager.EXTRA_SHOW_SPN, true);
+        stickyIntent.removeExtra(TelephonyManager.EXTRA_SPN);
+
+        mCarrierTextManager = new CarrierTextManager.Builder(
+                getContextSpyForStickyBroadcast(stickyIntent),
+                mContext.getResources(),
+                mWifiRepository,
+                mTelephonyManager,
+                mTelephonyListenerManager,
+                mWakefulnessLifecycle,
+                mMainExecutor,
+                mBgExecutor,
+                mKeyguardUpdateMonitor,
+                mLogger
+        )
+                .setShowAirplaneMode(true)
+                .setShowMissingSim(true)
+                .build();
+
+        // GIVEN - airplane mode is off (causing CTM to fetch the sticky broadcast)
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
+        reset(mCarrierTextCallback);
+        List<SubscriptionInfo> list = new ArrayList<>();
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getSimState(0))
+                .thenReturn(TelephonyManager.SIM_STATE_NOT_READY);
+        mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
+        // WHEN CTM fetches the broadcast and attempts to log the result, no crash results
+        mCarrierTextManager.updateCarrierText();
+
+        // No assert, this test should not crash
+    }
+
     @Test
     public void testCardIOError() {
         reset(mCarrierTextCallback);
@@ -506,4 +560,10 @@
         assertEquals(TEST_CARRIER + SEPARATOR + TEST_CARRIER,
                 captor.getValue().carrierText);
     }
+
+    private Context getContextSpyForStickyBroadcast(Intent returnVal) {
+        Context contextSpy = spy(mContext);
+        doReturn(returnVal).when(contextSpy).registerReceiver(eq(null), any(IntentFilter.class));
+        return contextSpy;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 1ba9931..ae3a320 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -40,6 +40,8 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -77,6 +79,7 @@
     @Mock
     private EmergencyButtonController mEmergencyButtonController;
 
+    private FakeFeatureFlags mFeatureFlags;
     private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
 
     @Before
@@ -90,10 +93,18 @@
         when(mAbsKeyInputView.requireViewById(R.id.bouncer_message_area))
                 .thenReturn(mKeyguardMessageArea);
         when(mAbsKeyInputView.getResources()).thenReturn(getContext().getResources());
-        mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
+        mFeatureFlags = new FakeFeatureFlags();
+        mFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false);
+        mKeyguardAbsKeyInputViewController = createTestObject();
+        mKeyguardAbsKeyInputViewController.init();
+        reset(mKeyguardMessageAreaController);  // Clear out implicit call to init.
+    }
+
+    private KeyguardAbsKeyInputViewController createTestObject() {
+        return new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
                 mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
                 mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector,
-                mEmergencyButtonController) {
+                mEmergencyButtonController, mFeatureFlags) {
             @Override
             void resetState() {
             }
@@ -108,8 +119,16 @@
                 return 0;
             }
         };
-        mKeyguardAbsKeyInputViewController.init();
-        reset(mKeyguardMessageAreaController);  // Clear out implicit call to init.
+    }
+
+    @Test
+    public void withFeatureFlagOn_oldMessage_isHidden() {
+        mFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true);
+        KeyguardAbsKeyInputViewController underTest = createTestObject();
+
+        underTest.init();
+
+        verify(mKeyguardMessageAreaController).disable();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index d8e2a38..fb73845 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -134,7 +134,6 @@
 
     private KeyguardClockSwitchController mController;
     private View mSliceView;
-    private LinearLayout mStatusArea;
     private FakeExecutor mExecutor;
 
     @Before
@@ -196,8 +195,8 @@
 
         mSliceView = new View(getContext());
         when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
-        mStatusArea = new LinearLayout(getContext());
-        when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea);
+        when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(
+                new LinearLayout(getContext()));
     }
 
     @Test
@@ -402,15 +401,6 @@
         assertNull(mController.getClock());
     }
 
-    @Test
-    public void testSetAlpha_setClockAlphaForCLockFace() {
-        mController.onViewAttached();
-        mController.setAlpha(0.5f);
-        verify(mLargeClockView).setAlpha(0.5f);
-        verify(mSmallClockView).setAlpha(0.5f);
-        assertEquals(0.5f, mStatusArea.getAlpha(), 0.0f);
-    }
-
     private void verifyAttachment(VerificationMode times) {
         verify(mClockRegistry, times).registerClockChangeListener(
                 any(ClockRegistry.ClockChangeListener.class));
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index 082c8cc..1a9260c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -26,6 +26,8 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.util.concurrency.DelayableExecutor
 import org.junit.Before
 import org.junit.Test
@@ -76,6 +78,8 @@
     Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry))
         .thenReturn(passwordEntry)
     `when`(keyguardPasswordView.resources).thenReturn(context.resources)
+    val fakeFeatureFlags = FakeFeatureFlags()
+    fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
     keyguardPasswordViewController =
         KeyguardPasswordViewController(
             keyguardPasswordView,
@@ -90,7 +94,8 @@
             mainExecutor,
             mContext.resources,
             falsingCollector,
-            keyguardViewController)
+            keyguardViewController,
+            fakeFeatureFlags)
   }
 
   @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index a8d5569..71a57c7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -26,6 +26,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.policy.DevicePostureController
 import org.junit.Before
 import org.junit.Test
@@ -72,6 +74,7 @@
   @Mock private lateinit var mPostureController: DevicePostureController
 
   private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
+  private lateinit var fakeFeatureFlags: FakeFeatureFlags
 
   @Before
   fun setup() {
@@ -86,6 +89,8 @@
     `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea))
         .thenReturn(mKeyguardMessageAreaController)
     `when`(mKeyguardPatternView.resources).thenReturn(context.resources)
+    fakeFeatureFlags = FakeFeatureFlags()
+    fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false)
     mKeyguardPatternViewController =
         KeyguardPatternViewController(
             mKeyguardPatternView,
@@ -97,7 +102,17 @@
             mFalsingCollector,
             mEmergencyButtonController,
             mKeyguardMessageAreaControllerFactory,
-            mPostureController)
+            mPostureController,
+            fakeFeatureFlags)
+  }
+
+  @Test
+  fun withFeatureFlagOn_oldMessage_isHidden() {
+    fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
+
+    mKeyguardPatternViewController.init()
+
+    verify<KeyguardMessageAreaController<*>>(mKeyguardMessageAreaController).disable()
   }
 
   @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 0881e61..cf86c21 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -36,6 +36,8 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.SingleTapClassifier;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -98,10 +100,13 @@
                 .thenReturn(mDeleteButton);
         when(mPinBasedInputView.findViewById(R.id.key_enter))
                 .thenReturn(mOkButton);
+        FakeFeatureFlags featureFlags = new FakeFeatureFlags();
+        featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true);
+
         mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
                 mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
                 mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
-                mEmergencyButtonController, mFalsingCollector) {
+                mEmergencyButtonController, mFalsingCollector, featureFlags) {
             @Override
             public void onResume(int reason) {
                 super.onResume(reason);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 65ddb53..1559c64 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -63,7 +63,9 @@
 import com.android.systemui.biometrics.SideFpsUiRequestSource;
 import com.android.systemui.classifier.FalsingA11yDelegate;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.ActivityStarter;
@@ -195,11 +197,15 @@
         when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
         when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(SecurityMode.PIN);
         when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
+        FakeFeatureFlags featureFlags = new FakeFeatureFlags();
+        featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true);
+
         mKeyguardPasswordViewController = new KeyguardPasswordViewController(
                 (KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
                 SecurityMode.Password, mLockPatternUtils, null,
                 mKeyguardMessageAreaControllerFactory, null, null, mEmergencyButtonController,
-                null, mock(Resources.class), null, mKeyguardViewController);
+                null, mock(Resources.class), null, mKeyguardViewController,
+                featureFlags);
 
         mKeyguardSecurityContainerController = new KeyguardSecurityContainerController(
                 mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index eb86c05..a3acc78 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.util.mockito.any
 import org.junit.Before
 import org.junit.Test
@@ -71,6 +73,9 @@
         simPinView =
             LayoutInflater.from(context).inflate(R.layout.keyguard_sim_pin_view, null)
                 as KeyguardSimPinView
+        val fakeFeatureFlags = FakeFeatureFlags()
+        fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
+
         underTest =
             KeyguardSimPinViewController(
                 simPinView,
@@ -83,7 +88,8 @@
                 liftToActivateListener,
                 telephonyManager,
                 falsingCollector,
-                emergencyButtonController
+                emergencyButtonController,
+                fakeFeatureFlags,
             )
         underTest.init()
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 2dcca55..efcf4dd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.util.mockito.any
 import org.junit.Before
 import org.junit.Test
@@ -70,6 +72,9 @@
         simPukView =
             LayoutInflater.from(context).inflate(R.layout.keyguard_sim_puk_view, null)
                 as KeyguardSimPukView
+        val fakeFeatureFlags = FakeFeatureFlags()
+        fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
+
         underTest =
             KeyguardSimPukViewController(
                 simPukView,
@@ -82,7 +87,8 @@
                 liftToActivateListener,
                 telephonyManager,
                 falsingCollector,
-                emergencyButtonController
+                emergencyButtonController,
+                fakeFeatureFlags,
             )
         underTest.init()
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
index a8c281c..f8262d4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
@@ -24,46 +24,33 @@
         get() = keyguardStatusView.findViewById(R.id.status_view_media_container)
     private val statusViewContainer: ViewGroup
         get() = keyguardStatusView.findViewById(R.id.status_view_container)
-    private val clockView: ViewGroup
-        get() = keyguardStatusView.findViewById(R.id.keyguard_clock_container)
     private val childrenExcludingMedia
         get() = statusViewContainer.children.filter { it != mediaView }
 
     @Before
     fun setUp() {
-        keyguardStatusView = LayoutInflater.from(context)
-                .inflate(R.layout.keyguard_status_view, /* root= */ null) as KeyguardStatusView
+        keyguardStatusView =
+            LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, /* root= */ null)
+                as KeyguardStatusView
     }
 
     @Test
     fun setChildrenTranslationYExcludingMediaView_mediaViewIsNotTranslated() {
         val translationY = 1234f
 
-        keyguardStatusView.setChildrenTranslationY(translationY, /* excludeMedia= */true)
+        keyguardStatusView.setChildrenTranslationY(translationY, /* excludeMedia= */ true)
 
         assertThat(mediaView.translationY).isEqualTo(0)
 
-        childrenExcludingMedia.forEach {
-            assertThat(it.translationY).isEqualTo(translationY)
-        }
+        childrenExcludingMedia.forEach { assertThat(it.translationY).isEqualTo(translationY) }
     }
 
     @Test
     fun setChildrenTranslationYIncludeMediaView() {
         val translationY = 1234f
 
-        keyguardStatusView.setChildrenTranslationY(translationY, /* excludeMedia= */false)
+        keyguardStatusView.setChildrenTranslationY(translationY, /* excludeMedia= */ false)
 
-        statusViewContainer.children.forEach {
-            assertThat(it.translationY).isEqualTo(translationY)
-        }
-    }
-
-    @Test
-    fun setAlphaExcludeClock() {
-        keyguardStatusView.setAlpha(0.5f, /* excludeClock= */true)
-        assertThat(statusViewContainer.alpha).isNotEqualTo(0.5f)
-        assertThat(mediaView.alpha).isEqualTo(0.5f)
-        assertThat(clockView.alpha).isNotEqualTo(0.5f)
+        statusViewContainer.children.forEach { assertThat(it.translationY).isEqualTo(translationY) }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactoryTest.kt
new file mode 100644
index 0000000..9e79849
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactoryTest.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.bouncer.data.factory
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Pattern
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.StringSubject
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BouncerMessageFactoryTest : SysuiTestCase() {
+    private lateinit var underTest: BouncerMessageFactory
+
+    @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
+
+    @Mock private lateinit var securityModel: KeyguardSecurityModel
+
+    private lateinit var testScope: TestScope
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testScope = TestScope()
+        underTest = BouncerMessageFactory(updateMonitor, securityModel)
+    }
+
+    @Test
+    fun bouncerMessages_choosesTheRightMessage_basedOnSecurityModeAndFpAllowedInBouncer() =
+        testScope.runTest {
+            primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAllowedInBouncer = false)
+                .isEqualTo("Enter PIN")
+            primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAllowedInBouncer = true)
+                .isEqualTo("Unlock with PIN or fingerprint")
+
+            primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAllowedInBouncer = false)
+                .isEqualTo("Enter password")
+            primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAllowedInBouncer = true)
+                .isEqualTo("Unlock with password or fingerprint")
+
+            primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAllowedInBouncer = false)
+                .isEqualTo("Draw pattern")
+            primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAllowedInBouncer = true)
+                .isEqualTo("Unlock with pattern or fingerprint")
+        }
+
+    @Test
+    fun bouncerMessages_setsPrimaryAndSecondaryMessage_basedOnSecurityModeAndFpAllowedInBouncer() =
+        testScope.runTest {
+            primaryMessage(
+                    PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
+                    mode = PIN,
+                    fpAllowedInBouncer = true
+                )
+                .isEqualTo("Wrong PIN. Try again.")
+            secondaryMessage(
+                    PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
+                    mode = PIN,
+                    fpAllowedInBouncer = true
+                )
+                .isEqualTo("Or unlock with fingerprint")
+
+            primaryMessage(
+                    PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
+                    mode = Password,
+                    fpAllowedInBouncer = true
+                )
+                .isEqualTo("Wrong password. Try again.")
+            secondaryMessage(
+                    PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
+                    mode = Password,
+                    fpAllowedInBouncer = true
+                )
+                .isEqualTo("Or unlock with fingerprint")
+
+            primaryMessage(
+                    PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
+                    mode = Pattern,
+                    fpAllowedInBouncer = true
+                )
+                .isEqualTo("Wrong pattern. Try again.")
+            secondaryMessage(
+                    PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
+                    mode = Pattern,
+                    fpAllowedInBouncer = true
+                )
+                .isEqualTo("Or unlock with fingerprint")
+        }
+
+    private fun primaryMessage(
+        reason: Int,
+        mode: KeyguardSecurityModel.SecurityMode,
+        fpAllowedInBouncer: Boolean
+    ): StringSubject {
+        return assertThat(
+            context.resources.getString(
+                bouncerMessageModel(mode, fpAllowedInBouncer, reason)!!.message!!.messageResId!!
+            )
+        )!!
+    }
+
+    private fun secondaryMessage(
+        reason: Int,
+        mode: KeyguardSecurityModel.SecurityMode,
+        fpAllowedInBouncer: Boolean
+    ): StringSubject {
+        return assertThat(
+            context.resources.getString(
+                bouncerMessageModel(mode, fpAllowedInBouncer, reason)!!
+                    .secondaryMessage!!
+                    .messageResId!!
+            )
+        )!!
+    }
+
+    private fun bouncerMessageModel(
+        mode: KeyguardSecurityModel.SecurityMode,
+        fpAllowedInBouncer: Boolean,
+        reason: Int
+    ): BouncerMessageModel? {
+        whenever(securityModel.getSecurityMode(0)).thenReturn(mode)
+        whenever(updateMonitor.isFingerprintAllowedInBouncer).thenReturn(fpAllowedInBouncer)
+
+        return underTest.createFromPromptReason(reason, 0)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepositoryTest.kt
new file mode 100644
index 0000000..1277fc0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepositoryTest.kt
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.bouncer.data.repository
+
+import android.content.pm.UserInfo
+import android.hardware.biometrics.BiometricSourceType
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.R
+import com.android.systemui.R.string.keyguard_enter_pin
+import com.android.systemui.R.string.kg_prompt_after_dpm_lock
+import com.android.systemui.R.string.kg_prompt_after_user_lockdown_pin
+import com.android.systemui.R.string.kg_prompt_auth_timeout
+import com.android.systemui.R.string.kg_prompt_pin_auth_timeout
+import com.android.systemui.R.string.kg_prompt_reason_restart_pin
+import com.android.systemui.R.string.kg_prompt_unattended_update
+import com.android.systemui.R.string.kg_trust_agent_disabled
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.bouncer.data.factory.BouncerMessageFactory
+import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
+import com.android.systemui.keyguard.bouncer.shared.model.Message
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.shared.model.AuthenticationFlags
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidJUnit4::class)
+class BouncerMessageRepositoryTest : SysuiTestCase() {
+
+    @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var securityModel: KeyguardSecurityModel
+    @Captor
+    private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
+
+    private lateinit var underTest: BouncerMessageRepository
+    private lateinit var trustRepository: FakeTrustRepository
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+    private lateinit var userRepository: FakeUserRepository
+    private lateinit var fingerprintRepository: FakeDeviceEntryFingerprintAuthRepository
+    private lateinit var testScope: TestScope
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        trustRepository = FakeTrustRepository()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+        userRepository = FakeUserRepository()
+        userRepository.setUserInfos(listOf(PRIMARY_USER))
+        fingerprintRepository = FakeDeviceEntryFingerprintAuthRepository()
+        testScope = TestScope()
+
+        whenever(updateMonitor.isFingerprintAllowedInBouncer).thenReturn(false)
+        whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN)
+        underTest =
+            BouncerMessageRepositoryImpl(
+                trustRepository = trustRepository,
+                biometricSettingsRepository = biometricSettingsRepository,
+                updateMonitor = updateMonitor,
+                bouncerMessageFactory = BouncerMessageFactory(updateMonitor, securityModel),
+                userRepository = userRepository,
+                fingerprintAuthRepository = fingerprintRepository
+            )
+    }
+
+    @Test
+    fun setCustomMessage_propagatesState() =
+        testScope.runTest {
+            underTest.setCustomMessage(message("not empty"))
+
+            val customMessage = collectLastValue(underTest.customMessage)
+
+            assertThat(customMessage()).isEqualTo(message("not empty"))
+        }
+
+    @Test
+    fun setFaceMessage_propagatesState() =
+        testScope.runTest {
+            underTest.setFaceAcquisitionMessage(message("not empty"))
+
+            val faceAcquisitionMessage = collectLastValue(underTest.faceAcquisitionMessage)
+
+            assertThat(faceAcquisitionMessage()).isEqualTo(message("not empty"))
+        }
+
+    @Test
+    fun setFpMessage_propagatesState() =
+        testScope.runTest {
+            underTest.setFingerprintAcquisitionMessage(message("not empty"))
+
+            val fpAcquisitionMsg = collectLastValue(underTest.fingerprintAcquisitionMessage)
+
+            assertThat(fpAcquisitionMsg()).isEqualTo(message("not empty"))
+        }
+
+    @Test
+    fun setPrimaryAuthMessage_propagatesState() =
+        testScope.runTest {
+            underTest.setPrimaryAuthMessage(message("not empty"))
+
+            val primaryAuthMessage = collectLastValue(underTest.primaryAuthMessage)
+
+            assertThat(primaryAuthMessage()).isEqualTo(message("not empty"))
+        }
+
+    @Test
+    fun biometricAuthMessage_propagatesBiometricAuthMessages() =
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(PRIMARY_USER)
+            val biometricAuthMessage = collectLastValue(underTest.biometricAuthMessage)
+            runCurrent()
+
+            verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
+
+            updateMonitorCallback.value.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT)
+
+            assertThat(biometricAuthMessage())
+                .isEqualTo(message(R.string.kg_fp_not_recognized, R.string.kg_bio_try_again_or_pin))
+
+            updateMonitorCallback.value.onBiometricAuthFailed(BiometricSourceType.FACE)
+
+            assertThat(biometricAuthMessage())
+                .isEqualTo(
+                    message(R.string.bouncer_face_not_recognized, R.string.kg_bio_try_again_or_pin)
+                )
+
+            updateMonitorCallback.value.onBiometricAcquired(BiometricSourceType.FACE, 0)
+
+            assertThat(biometricAuthMessage()).isNull()
+        }
+
+    @Test
+    fun onFaceLockout_propagatesState() =
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(PRIMARY_USER)
+            val lockoutMessage = collectLastValue(underTest.biometricLockedOutMessage)
+            runCurrent()
+            verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
+
+            whenever(updateMonitor.isFaceLockedOut).thenReturn(true)
+            updateMonitorCallback.value.onLockedOutStateChanged(BiometricSourceType.FACE)
+
+            assertThat(lockoutMessage())
+                .isEqualTo(message(keyguard_enter_pin, R.string.kg_face_locked_out))
+
+            whenever(updateMonitor.isFaceLockedOut).thenReturn(false)
+            updateMonitorCallback.value.onLockedOutStateChanged(BiometricSourceType.FACE)
+            assertThat(lockoutMessage()).isNull()
+        }
+
+    @Test
+    fun onFingerprintLockout_propagatesState() =
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(PRIMARY_USER)
+            val lockedOutMessage = collectLastValue(underTest.biometricLockedOutMessage)
+            runCurrent()
+
+            fingerprintRepository.setLockedOut(true)
+
+            assertThat(lockedOutMessage())
+                .isEqualTo(message(keyguard_enter_pin, R.string.kg_fp_locked_out))
+
+            fingerprintRepository.setLockedOut(false)
+            assertThat(lockedOutMessage()).isNull()
+        }
+
+    @Test
+    fun onAuthFlagsChanged_withTrustNotManagedAndNoBiometrics_isANoop() =
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(PRIMARY_USER)
+            trustRepository.setCurrentUserTrustManaged(false)
+            biometricSettingsRepository.setFaceEnrolled(false)
+            biometricSettingsRepository.setFingerprintEnrolled(false)
+
+            verifyMessagesForAuthFlag(
+                STRONG_AUTH_NOT_REQUIRED to null,
+                STRONG_AUTH_REQUIRED_AFTER_BOOT to null,
+                SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
+                STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
+                STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to null,
+                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to null,
+                STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to null,
+                SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
+                STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to null,
+                STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+                    Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
+            )
+        }
+
+    @Test
+    fun authFlagsChanges_withTrustManaged_providesDifferentMessages() =
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(PRIMARY_USER)
+            biometricSettingsRepository.setFaceEnrolled(false)
+            biometricSettingsRepository.setFingerprintEnrolled(false)
+
+            trustRepository.setCurrentUserTrustManaged(true)
+
+            verifyMessagesForAuthFlag(
+                STRONG_AUTH_NOT_REQUIRED to null,
+                STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
+                STRONG_AUTH_REQUIRED_AFTER_BOOT to
+                    Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
+                STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
+                    Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
+                STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+                    Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
+                SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
+                    Pair(keyguard_enter_pin, kg_trust_agent_disabled),
+                SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
+                    Pair(keyguard_enter_pin, kg_trust_agent_disabled),
+                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
+                    Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
+                STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
+                    Pair(keyguard_enter_pin, kg_prompt_unattended_update),
+                STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
+                    Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
+            )
+        }
+
+    @Test
+    fun authFlagsChanges_withFaceEnrolled_providesDifferentMessages() =
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(PRIMARY_USER)
+            trustRepository.setCurrentUserTrustManaged(false)
+            biometricSettingsRepository.setFingerprintEnrolled(false)
+
+            biometricSettingsRepository.setIsFaceAuthEnabled(true)
+            biometricSettingsRepository.setFaceEnrolled(true)
+
+            verifyMessagesForAuthFlag(
+                STRONG_AUTH_NOT_REQUIRED to null,
+                STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
+                SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
+                SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
+                STRONG_AUTH_REQUIRED_AFTER_BOOT to
+                    Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
+                STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
+                    Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
+                STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+                    Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
+                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
+                    Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
+                STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
+                    Pair(keyguard_enter_pin, kg_prompt_unattended_update),
+                STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
+                    Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
+            )
+        }
+
+    @Test
+    fun authFlagsChanges_withFingerprintEnrolled_providesDifferentMessages() =
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(PRIMARY_USER)
+            trustRepository.setCurrentUserTrustManaged(false)
+            biometricSettingsRepository.setIsFaceAuthEnabled(false)
+            biometricSettingsRepository.setFaceEnrolled(false)
+
+            biometricSettingsRepository.setFingerprintEnrolled(true)
+            biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true)
+
+            verifyMessagesForAuthFlag(
+                STRONG_AUTH_NOT_REQUIRED to null,
+                STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
+                SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
+                SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
+                STRONG_AUTH_REQUIRED_AFTER_BOOT to
+                    Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
+                STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
+                    Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
+                STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+                    Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
+                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
+                    Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
+                STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
+                    Pair(keyguard_enter_pin, kg_prompt_unattended_update),
+                STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
+                    Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
+            )
+        }
+
+    private fun TestScope.verifyMessagesForAuthFlag(
+        vararg authFlagToExpectedMessages: Pair<Int, Pair<Int, Int>?>
+    ) {
+        val authFlagsMessage = collectLastValue(underTest.authFlagsMessage)
+
+        authFlagToExpectedMessages.forEach { (flag, messagePair) ->
+            biometricSettingsRepository.setAuthenticationFlags(
+                AuthenticationFlags(PRIMARY_USER_ID, flag)
+            )
+
+            assertThat(authFlagsMessage())
+                .isEqualTo(messagePair?.let { message(it.first, it.second) })
+        }
+    }
+
+    private fun message(primaryResId: Int, secondaryResId: Int): BouncerMessageModel {
+        return BouncerMessageModel(
+            message = Message(messageResId = primaryResId),
+            secondaryMessage = Message(messageResId = secondaryResId)
+        )
+    }
+    private fun message(value: String): BouncerMessageModel {
+        return BouncerMessageModel(message = Message(message = value))
+    }
+
+    companion object {
+        private const val PRIMARY_USER_ID = 0
+        private val PRIMARY_USER =
+            UserInfo(
+                /* id= */ PRIMARY_USER_ID,
+                /* name= */ "primary user",
+                /* flags= */ UserInfo.FLAG_PRIMARY
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
new file mode 100644
index 0000000..b0af310
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.bouncer.domain.interactor
+
+import android.content.pm.UserInfo
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R.string.keyguard_enter_pin
+import com.android.systemui.R.string.kg_too_many_failed_attempts_countdown
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.FlowValue
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.bouncer.data.factory.BouncerMessageFactory
+import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
+import com.android.systemui.keyguard.bouncer.shared.model.Message
+import com.android.systemui.keyguard.data.repository.FakeBouncerMessageRepository
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.KotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidJUnit4::class)
+class BouncerMessageInteractorTest : SysuiTestCase() {
+
+    @Mock private lateinit var securityModel: KeyguardSecurityModel
+    @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var countDownTimerUtil: CountDownTimerUtil
+    private lateinit var countDownTimerCallback: KotlinArgumentCaptor<CountDownTimerCallback>
+    private lateinit var underTest: BouncerMessageInteractor
+    private lateinit var repository: FakeBouncerMessageRepository
+    private lateinit var userRepository: FakeUserRepository
+    private lateinit var testScope: TestScope
+    private lateinit var bouncerMessage: FlowValue<BouncerMessageModel?>
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        repository = FakeBouncerMessageRepository()
+        userRepository = FakeUserRepository()
+        userRepository.setUserInfos(listOf(PRIMARY_USER))
+        testScope = TestScope()
+        countDownTimerCallback = KotlinArgumentCaptor(CountDownTimerCallback::class.java)
+
+        allowTestableLooperAsMainThread()
+        whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN)
+        whenever(updateMonitor.isFingerprintAllowedInBouncer).thenReturn(false)
+    }
+
+    suspend fun TestScope.init() {
+        userRepository.setSelectedUserInfo(PRIMARY_USER)
+        val featureFlags = FakeFeatureFlags()
+        featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
+        underTest =
+            BouncerMessageInteractor(
+                repository = repository,
+                factory = BouncerMessageFactory(updateMonitor, securityModel),
+                userRepository = userRepository,
+                countDownTimerUtil = countDownTimerUtil,
+                featureFlags = featureFlags
+            )
+        bouncerMessage = collectLastValue(underTest.bouncerMessage)
+    }
+
+    @Test
+    fun onIncorrectSecurityInput_setsTheBouncerModelInTheRepository() =
+        testScope.runTest {
+            init()
+            underTest.onPrimaryAuthIncorrectAttempt()
+
+            assertThat(repository.primaryAuthMessage).isNotNull()
+            assertThat(
+                    context.resources.getString(
+                        repository.primaryAuthMessage.value!!.message!!.messageResId!!
+                    )
+                )
+                .isEqualTo("Wrong PIN. Try again.")
+        }
+
+    @Test
+    fun onUserStartsPrimaryAuthInput_clearsAllSetBouncerMessages() =
+        testScope.runTest {
+            init()
+            repository.setCustomMessage(message("not empty"))
+            repository.setFaceAcquisitionMessage(message("not empty"))
+            repository.setFingerprintAcquisitionMessage(message("not empty"))
+            repository.setPrimaryAuthMessage(message("not empty"))
+
+            underTest.onPrimaryBouncerUserInput()
+
+            assertThat(repository.customMessage.value).isNull()
+            assertThat(repository.faceAcquisitionMessage.value).isNull()
+            assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
+            assertThat(repository.primaryAuthMessage.value).isNull()
+        }
+
+    @Test
+    fun onBouncerBeingHidden_clearsAllSetBouncerMessages() =
+        testScope.runTest {
+            init()
+            repository.setCustomMessage(message("not empty"))
+            repository.setFaceAcquisitionMessage(message("not empty"))
+            repository.setFingerprintAcquisitionMessage(message("not empty"))
+            repository.setPrimaryAuthMessage(message("not empty"))
+
+            underTest.onBouncerBeingHidden()
+
+            assertThat(repository.customMessage.value).isNull()
+            assertThat(repository.faceAcquisitionMessage.value).isNull()
+            assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
+            assertThat(repository.primaryAuthMessage.value).isNull()
+        }
+
+    @Test
+    fun setCustomMessage_setsRepositoryValue() =
+        testScope.runTest {
+            init()
+
+            underTest.setCustomMessage("not empty")
+
+            assertThat(repository.customMessage.value)
+                .isEqualTo(BouncerMessageModel(secondaryMessage = Message(message = "not empty")))
+
+            underTest.setCustomMessage(null)
+            assertThat(repository.customMessage.value).isNull()
+        }
+
+    @Test
+    fun setFaceMessage_setsRepositoryValue() =
+        testScope.runTest {
+            init()
+
+            underTest.setFaceAcquisitionMessage("not empty")
+
+            assertThat(repository.faceAcquisitionMessage.value)
+                .isEqualTo(BouncerMessageModel(secondaryMessage = Message(message = "not empty")))
+
+            underTest.setFaceAcquisitionMessage(null)
+            assertThat(repository.faceAcquisitionMessage.value).isNull()
+        }
+
+    @Test
+    fun setFingerprintMessage_setsRepositoryValue() =
+        testScope.runTest {
+            init()
+
+            underTest.setFingerprintAcquisitionMessage("not empty")
+
+            assertThat(repository.fingerprintAcquisitionMessage.value)
+                .isEqualTo(BouncerMessageModel(secondaryMessage = Message(message = "not empty")))
+
+            underTest.setFingerprintAcquisitionMessage(null)
+            assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
+        }
+
+    @Test
+    fun onPrimaryAuthLockout_startsTimerForSpecifiedNumberOfSeconds() =
+        testScope.runTest {
+            init()
+
+            underTest.onPrimaryAuthLockedOut(3)
+
+            verify(countDownTimerUtil)
+                .startNewTimer(eq(3000L), eq(1000L), countDownTimerCallback.capture())
+
+            countDownTimerCallback.value.onTick(2000L)
+
+            val primaryMessage = repository.primaryAuthMessage.value!!.message!!
+            assertThat(primaryMessage.messageResId!!)
+                .isEqualTo(kg_too_many_failed_attempts_countdown)
+            assertThat(primaryMessage.formatterArgs).isEqualTo(mapOf(Pair("count", 2)))
+        }
+
+    @Test
+    fun onPrimaryAuthLockout_timerComplete_resetsRepositoryMessages() =
+        testScope.runTest {
+            init()
+            repository.setCustomMessage(message("not empty"))
+            repository.setFaceAcquisitionMessage(message("not empty"))
+            repository.setFingerprintAcquisitionMessage(message("not empty"))
+            repository.setPrimaryAuthMessage(message("not empty"))
+
+            underTest.onPrimaryAuthLockedOut(3)
+
+            verify(countDownTimerUtil)
+                .startNewTimer(eq(3000L), eq(1000L), countDownTimerCallback.capture())
+
+            countDownTimerCallback.value.onFinish()
+
+            assertThat(repository.customMessage.value).isNull()
+            assertThat(repository.faceAcquisitionMessage.value).isNull()
+            assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
+            assertThat(repository.primaryAuthMessage.value).isNull()
+        }
+
+    @Test
+    fun bouncerMessage_hasPriorityOrderOfMessages() =
+        testScope.runTest {
+            init()
+            repository.setBiometricAuthMessage(message("biometric message"))
+            repository.setFaceAcquisitionMessage(message("face acquisition message"))
+            repository.setFingerprintAcquisitionMessage(message("fingerprint acquisition message"))
+            repository.setPrimaryAuthMessage(message("primary auth message"))
+            repository.setAuthFlagsMessage(message("auth flags message"))
+            repository.setBiometricLockedOutMessage(message("biometrics locked out"))
+            repository.setCustomMessage(message("custom message"))
+
+            assertThat(bouncerMessage()).isEqualTo(message("primary auth message"))
+
+            repository.setPrimaryAuthMessage(null)
+
+            assertThat(bouncerMessage()).isEqualTo(message("biometric message"))
+
+            repository.setBiometricAuthMessage(null)
+
+            assertThat(bouncerMessage()).isEqualTo(message("fingerprint acquisition message"))
+
+            repository.setFingerprintAcquisitionMessage(null)
+
+            assertThat(bouncerMessage()).isEqualTo(message("face acquisition message"))
+
+            repository.setFaceAcquisitionMessage(null)
+
+            assertThat(bouncerMessage()).isEqualTo(message("custom message"))
+
+            repository.setCustomMessage(null)
+
+            assertThat(bouncerMessage()).isEqualTo(message("auth flags message"))
+
+            repository.setAuthFlagsMessage(null)
+
+            assertThat(bouncerMessage()).isEqualTo(message("biometrics locked out"))
+
+            repository.setBiometricLockedOutMessage(null)
+
+            // sets the default message if everything else is null
+            assertThat(bouncerMessage()!!.message!!.messageResId).isEqualTo(keyguard_enter_pin)
+        }
+
+    private fun message(value: String): BouncerMessageModel {
+        return BouncerMessageModel(message = Message(message = value))
+    }
+
+    companion object {
+        private const val PRIMARY_USER_ID = 0
+        private val PRIMARY_USER =
+            UserInfo(
+                /* id= */ PRIMARY_USER_ID,
+                /* name= */ "primary user",
+                /* flags= */ UserInfo.FLAG_PRIMARY
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 1bab817..b925aeb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -310,19 +310,38 @@
             createBiometricSettingsRepository()
             verify(biometricManager)
                 .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
-
             whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID)))
                 .thenReturn(0)
             broadcastDPMStateChange()
-
-            biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID)
             val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled)
 
-            assertThat(isFaceAuthEnabled()).isTrue()
+            assertThat(isFaceAuthEnabled()).isFalse()
 
-            biometricManagerCallback.value.onChanged(false, PRIMARY_USER_ID)
+            // Value changes for another user
+            biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID)
 
             assertThat(isFaceAuthEnabled()).isFalse()
+
+            // Value changes for current user.
+            biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID)
+
+            assertThat(isFaceAuthEnabled()).isTrue()
+        }
+
+    @Test
+    fun userChange_biometricEnabledChange_handlesRaceCondition() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+            verify(biometricManager)
+                .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
+            val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled)
+            biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID)
+            runCurrent()
+
+            userRepository.setSelectedUserInfo(ANOTHER_USER)
+            runCurrent()
+
+            assertThat(isFaceAuthEnabled()).isTrue()
         }
 
     @Test
@@ -382,7 +401,7 @@
         }
 
     @Test
-    fun userInLockdownUsesStrongAuthFlagsToDetermineValue() =
+    fun userInLockdownUsesAuthFlagsToDetermineValue() =
         testScope.runTest {
             createBiometricSettingsRepository()
 
@@ -405,6 +424,38 @@
             assertThat(isUserInLockdown()).isTrue()
         }
 
+    @Test
+    fun authFlagChangesForCurrentUserArePropagated() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+
+            val authFlags = collectLastValue(underTest.authenticationFlags)
+            // has default value.
+            val defaultStrongAuthValue = STRONG_AUTH_REQUIRED_AFTER_BOOT
+            assertThat(authFlags()!!.flag).isEqualTo(defaultStrongAuthValue)
+
+            // change strong auth flags for another user.
+            // Combine with one more flag to check if we do the bitwise and
+            val inLockdown =
+                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN or STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+            onStrongAuthChanged(inLockdown, ANOTHER_USER_ID)
+
+            // Still false.
+            assertThat(authFlags()!!.flag).isEqualTo(defaultStrongAuthValue)
+
+            // change strong auth flags for current user.
+            onStrongAuthChanged(inLockdown, PRIMARY_USER_ID)
+
+            assertThat(authFlags()!!.flag).isEqualTo(inLockdown)
+
+            onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, ANOTHER_USER_ID)
+
+            assertThat(authFlags()!!.flag).isEqualTo(inLockdown)
+
+            userRepository.setSelectedUserInfo(ANOTHER_USER)
+            assertThat(authFlags()!!.flag).isEqualTo(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT)
+        }
+
     private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) {
         authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index 1e465c7..68c33fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -530,6 +530,8 @@
         verify(collapsedSet).setVisibility(R.id.actionPlayPause, ConstraintSet.VISIBLE)
 
         assertThat(actionNext.isEnabled()).isTrue()
+        assertThat(actionNext.isFocusable()).isTrue()
+        assertThat(actionNext.isClickable()).isTrue()
         assertThat(actionNext.contentDescription).isEqualTo("next")
         verify(collapsedSet).setVisibility(R.id.actionNext, ConstraintSet.VISIBLE)
 
@@ -576,6 +578,8 @@
 
         assertThat(actionPrev.isEnabled()).isFalse()
         assertThat(actionPrev.drawable).isNull()
+        assertThat(actionPrev.isFocusable()).isFalse()
+        assertThat(actionPrev.isClickable()).isFalse()
         verify(expandedSet).setVisibility(R.id.actionPrev, ConstraintSet.INVISIBLE)
 
         assertThat(actionNext.isEnabled()).isFalse()
@@ -610,6 +614,8 @@
 
         assertThat(actionNext.isEnabled()).isFalse()
         assertThat(actionNext.drawable).isNull()
+        assertThat(actionNext.isFocusable()).isFalse()
+        assertThat(actionNext.isClickable()).isFalse()
         verify(expandedSet).setVisibility(R.id.actionNext, ConstraintSet.INVISIBLE)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 9b26d9c..c89897c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -22,6 +22,7 @@
 
 import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_GO_TO_APP;
 import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_NONE;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -94,10 +95,7 @@
 
     @Before
     public void setUp() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(false);
-        when(mMediaOutputController.isSubStatusSupported()).thenReturn(false);
         when(mMediaOutputController.getMediaItemList()).thenReturn(mMediaItems);
-        when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
         when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
         when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(false);
         when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
@@ -126,85 +124,24 @@
     }
 
     @Test
-    public void getItemCount_containExtraOneForPairNew() {
-        assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size() + 1);
-    }
-
-    @Test
-    public void getItemId_validPosition_returnCorrespondingId() {
-        assertThat(mMediaOutputAdapter.getItemId(0)).isEqualTo(mMediaDevices.get(
-                0).getId().hashCode());
-    }
-
-    @Test
-    public void getItemId_invalidPosition_returnPosition() {
-        int invalidPosition = mMediaDevices.size() + 1;
-        assertThat(mMediaOutputAdapter.getItemId(invalidPosition)).isEqualTo(invalidPosition);
-    }
-
-    @Test
-    public void advanced_getItemCount_returnsMediaItemSize() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
+    public void getItemCount_returnsMediaItemSize() {
         assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaItems.size());
     }
 
     @Test
-    public void advanced_getItemId_validPosition_returnCorrespondingId() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
+    public void getItemId_validPosition_returnCorrespondingId() {
         assertThat(mMediaOutputAdapter.getItemId(0)).isEqualTo(mMediaItems.get(
                 0).getMediaDevice().get().getId().hashCode());
     }
 
     @Test
-    public void advanced_getItemId_invalidPosition_returnPosition() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
+    public void getItemId_invalidPosition_returnPosition() {
         int invalidPosition = mMediaItems.size() + 1;
         assertThat(mMediaOutputAdapter.getItemId(invalidPosition)).isEqualTo(invalidPosition);
     }
 
     @Test
     public void onBindViewHolder_bindPairNew_verifyView() {
-        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
-
-        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText(
-                R.string.media_output_dialog_pairing_new));
-    }
-
-    @Test
-    public void onBindViewHolder_bindGroup_withSessionName_verifyView() {
-        when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
-        when(mMediaOutputController.getSessionName()).thenReturn(TEST_SESSION_NAME);
-        mMediaOutputAdapter.getItemCount();
-        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
-
-        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void onBindViewHolder_bindGroup_noSessionName_verifyView() {
-        when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
-        when(mMediaOutputController.getSessionName()).thenReturn(null);
-        mMediaOutputAdapter.getItemCount();
-        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
-
-        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void advanced_onBindViewHolder_bindPairNew_verifyView() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
         mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
@@ -222,8 +159,7 @@
     }
 
     @Test
-    public void advanced_onBindViewHolder_bindGroup_withSessionName_verifyView() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
+    public void onBindViewHolder_bindGroup_withSessionName_verifyView() {
         when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(
                 mMediaItems.stream().map((item) -> item.getMediaDevice().get()).collect(
                         Collectors.toList()));
@@ -243,8 +179,7 @@
     }
 
     @Test
-    public void advanced_onBindViewHolder_bindGroup_noSessionName_verifyView() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
+    public void onBindViewHolder_bindGroup_noSessionName_verifyView() {
         when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(
                 mMediaItems.stream().map((item) -> item.getMediaDevice().get()).collect(
                         Collectors.toList()));
@@ -277,8 +212,7 @@
     }
 
     @Test
-    public void advanced_onBindViewHolder_bindNonRemoteConnectedDevice_verifyView() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
+    public void onBindViewHolder_bindNonRemoteConnectedDevice_verifyView() {
         when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(false);
         mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
@@ -294,8 +228,7 @@
     }
 
     @Test
-    public void advanced_onBindViewHolder_bindConnectedRemoteDevice_verifyView() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
+    public void onBindViewHolder_bindConnectedRemoteDevice_verifyView() {
         when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(
                 ImmutableList.of(mMediaDevice2));
         when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(true);
@@ -314,8 +247,7 @@
     }
 
     @Test
-    public void advanced_onBindViewHolder_bindSingleConnectedRemoteDevice_verifyView() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
+    public void onBindViewHolder_bindSingleConnectedRemoteDevice_verifyView() {
         when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(
                 ImmutableList.of());
         when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(true);
@@ -347,8 +279,7 @@
     }
 
     @Test
-    public void advanced_onBindViewHolder_isMutingExpectedDevice_verifyView() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
+    public void onBindViewHolder_isMutingExpectedDevice_verifyView() {
         when(mMediaDevice1.isMutingExpectedDevice()).thenReturn(true);
         when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(false);
         when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(false);
@@ -449,8 +380,6 @@
 
     @Test
     public void subStatusSupported_onBindViewHolder_bindHostDeviceWithOngoingSession_verifyView() {
-        when(mMediaOutputController.isSubStatusSupported()).thenReturn(true);
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
         when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(true);
         when(mMediaDevice1.isHostForOngoingSession()).thenReturn(true);
         when(mMediaDevice1.hasSubtext()).thenReturn(true);
@@ -480,8 +409,6 @@
     public void subStatusSupported_onBindViewHolder_bindDeviceRequirePremium_verifyView() {
         String deviceStatus = (String) mContext.getText(
                 R.string.media_output_status_require_premium);
-        when(mMediaOutputController.isSubStatusSupported()).thenReturn(true);
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
         when(mMediaDevice2.hasSubtext()).thenReturn(true);
         when(mMediaDevice2.getSubtext()).thenReturn(SUBTEXT_SUBSCRIPTION_REQUIRED);
         when(mMediaDevice2.getSubtextString()).thenReturn(deviceStatus);
@@ -506,8 +433,6 @@
     public void subStatusSupported_onBindViewHolder_bindDeviceWithAdPlaying_verifyView() {
         String deviceStatus = (String) mContext.getText(
                 R.string.media_output_status_try_after_ad);
-        when(mMediaOutputController.isSubStatusSupported()).thenReturn(true);
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
         when(mMediaDevice2.hasSubtext()).thenReturn(true);
         when(mMediaDevice2.getSubtext()).thenReturn(SUBTEXT_AD_ROUTING_DISALLOWED);
         when(mMediaDevice2.getSubtextString()).thenReturn(deviceStatus);
@@ -531,8 +456,6 @@
 
     @Test
     public void subStatusSupported_onBindViewHolder_bindDeviceWithOngoingSession_verifyView() {
-        when(mMediaOutputController.isSubStatusSupported()).thenReturn(true);
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
         when(mMediaDevice1.hasSubtext()).thenReturn(true);
         when(mMediaDevice1.getSubtext()).thenReturn(SUBTEXT_CUSTOM);
         when(mMediaDevice1.getSubtextString()).thenReturn(TEST_CUSTOM_SUBTEXT);
@@ -603,15 +526,6 @@
 
     @Test
     public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
-        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
-        mViewHolder.mContainerLayout.performClick();
-
-        verify(mMediaOutputController).launchBluetoothPairing(mViewHolder.mContainerLayout);
-    }
-
-    @Test
-    public void advanced_onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
         mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
@@ -629,6 +543,12 @@
         when(mMediaOutputController.isCurrentOutputDeviceHasSessionOngoing()).thenReturn(false);
         assertThat(mMediaDevice2.getState()).isEqualTo(
                 LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+        when(mMediaDevice2.getSelectionBehavior()).thenReturn(SELECTION_BEHAVIOR_TRANSFER);
+        mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
+        mMediaOutputAdapter.updateItems();
+        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+                .onCreateViewHolder(new LinearLayout(mContext), 0);
+        mMediaOutputAdapter.getItemCount();
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
         mViewHolder.mContainerLayout.performClick();
@@ -641,7 +561,13 @@
         when(mMediaOutputController.isCurrentOutputDeviceHasSessionOngoing()).thenReturn(true);
         assertThat(mMediaDevice2.getState()).isEqualTo(
                 LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+        when(mMediaDevice2.getSelectionBehavior()).thenReturn(SELECTION_BEHAVIOR_TRANSFER);
+        mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
+        mMediaOutputAdapter.updateItems();
+        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+                .onCreateViewHolder(new LinearLayout(mContext), 0);
         MediaOutputAdapter.MediaDeviceViewHolder spyMediaDeviceViewHolder = spy(mViewHolder);
+        mMediaOutputAdapter.getItemCount();
 
         mMediaOutputAdapter.onBindViewHolder(spyMediaDeviceViewHolder, 0);
         mMediaOutputAdapter.onBindViewHolder(spyMediaDeviceViewHolder, 1);
@@ -665,20 +591,7 @@
     }
 
     @Test
-    public void onItemClick_clicksSelectableDevice_triggerGrouping() {
-        List<MediaDevice> selectableDevices = new ArrayList<>();
-        selectableDevices.add(mMediaDevice2);
-        when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices);
-        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
-
-        mViewHolder.mContainerLayout.performClick();
-
-        verify(mMediaOutputController).addDeviceToPlayMedia(mMediaDevice2);
-    }
-
-    @Test
-    public void advanced_onGroupActionTriggered_clicksEndAreaOfSelectableDevice_triggerGrouping() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
+    public void onGroupActionTriggered_clicksEndAreaOfSelectableDevice_triggerGrouping() {
         List<MediaDevice> selectableDevices = new ArrayList<>();
         selectableDevices.add(mMediaDevice2);
         when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices);
@@ -692,8 +605,7 @@
     }
 
     @Test
-    public void advanced_onGroupActionTriggered_clickSelectedRemoteDevice_triggerUngrouping() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
+    public void onGroupActionTriggered_clickSelectedRemoteDevice_triggerUngrouping() {
         when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(
                 ImmutableList.of(mMediaDevice2));
         when(mMediaOutputController.getDeselectableMediaDevice()).thenReturn(
@@ -710,21 +622,6 @@
 
     @Test
     public void onItemClick_onGroupActionTriggered_verifySeekbarDisabled() {
-        when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
-        List<MediaDevice> selectableDevices = new ArrayList<>();
-        selectableDevices.add(mMediaDevice1);
-        when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices);
-        when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(true);
-        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
-
-        mViewHolder.mContainerLayout.performClick();
-
-        assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
-    }
-
-    @Test
-    public void advanced_onItemClick_onGroupActionTriggered_verifySeekbarDisabled() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
         when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(
                 mMediaItems.stream().map((item) -> item.getMediaDevice().get()).collect(
                         Collectors.toList()));
@@ -767,7 +664,6 @@
 
     @Test
     public void updateItems_controllerItemsUpdated_notUpdatesInAdapterUntilUpdateItems() {
-        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
         mMediaOutputAdapter.updateItems();
         List<MediaItem> updatedList = new ArrayList<>();
         updatedList.add(new MediaItem());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 8f7bad6..9f06b5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -76,7 +76,6 @@
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
@@ -200,8 +199,6 @@
                 mNotifCollection, mDialogLaunchAnimator,
                 Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
-        when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(false);
-        when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ROUTES_PROCESSING)).thenReturn(false);
         mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
         when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
@@ -402,24 +399,9 @@
     }
 
     @Test
-    public void onDeviceListUpdate_verifyDeviceListCallback() {
-        mMediaOutputController.start(mCb);
-        reset(mCb);
-
-        mMediaOutputController.onDeviceListUpdate(mMediaDevices);
-        final List<MediaDevice> devices = new ArrayList<>(mMediaOutputController.getMediaDevices());
-
-        assertThat(devices.containsAll(mMediaDevices)).isTrue();
-        assertThat(devices.size()).isEqualTo(mMediaDevices.size());
-        verify(mCb).onDeviceListChanged();
-    }
-
-    @Test
     public void routeProcessSupport_onDeviceListUpdate_preferenceExist_NotUpdatesRangeInformation()
             throws RemoteException {
         when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(true);
-        when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ROUTES_PROCESSING)).thenReturn(true);
-        when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
         mMediaOutputController.start(mCb);
         reset(mCb);
 
@@ -431,8 +413,7 @@
     }
 
     @Test
-    public void advanced_onDeviceListUpdate_verifyDeviceListCallback() {
-        when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
+    public void onDeviceListUpdate_verifyDeviceListCallback() {
         mMediaOutputController.start(mCb);
         reset(mCb);
 
@@ -453,7 +434,6 @@
 
     @Test
     public void advanced_onDeviceListUpdateWithConnectedDeviceRemote_verifyItemSize() {
-        when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
         when(mMediaDevice1.getFeatures()).thenReturn(
                 ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK));
         when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1);
@@ -477,7 +457,6 @@
 
     @Test
     public void advanced_categorizeMediaItems_withSuggestedDevice_verifyDeviceListSize() {
-        when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
         when(mMediaDevice1.isSuggestedDevice()).thenReturn(true);
         when(mMediaDevice2.isSuggestedDevice()).thenReturn(false);
 
@@ -515,7 +494,6 @@
 
     @Test
     public void advanced_onDeviceListUpdate_isRefreshing_updatesNeedRefreshToTrue() {
-        when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
         mMediaOutputController.start(mCb);
         reset(mCb);
         mMediaOutputController.mIsRefreshing = true;
@@ -717,7 +695,6 @@
 
     @Test
     public void isAnyDeviceTransferring_advancedLayoutSupport() {
-        when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
         when(mMediaDevice1.getState()).thenReturn(
                 LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
         mMediaOutputController.start(mCb);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index ca2b1da..349fac0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -22,6 +22,7 @@
 import android.graphics.drawable.Drawable
 import android.media.MediaRoute2Info
 import android.os.PowerManager
+import android.os.VibrationAttributes
 import android.os.VibrationEffect
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
@@ -210,7 +211,14 @@
         assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
         assertThat(uiEventLoggerFake.eventId(0))
             .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST.id)
-        verify(vibratorHelper).vibrate(any<VibrationEffect>())
+        verify(vibratorHelper)
+            .vibrate(
+                any(),
+                any(),
+                any<VibrationEffect>(),
+                any(),
+                any<VibrationAttributes>(),
+            )
     }
 
     @Test
@@ -246,7 +254,14 @@
         assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
         assertThat(uiEventLoggerFake.eventId(0))
             .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST.id)
-        verify(vibratorHelper).vibrate(any<VibrationEffect>())
+        verify(vibratorHelper)
+            .vibrate(
+                any(),
+                any(),
+                any<VibrationEffect>(),
+                any(),
+                any<VibrationAttributes>(),
+            )
     }
 
     @Test
@@ -267,7 +282,14 @@
         assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
         assertThat(uiEventLoggerFake.eventId(0))
             .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED.id)
-        verify(vibratorHelper).vibrate(any<VibrationEffect>())
+        verify(vibratorHelper)
+            .vibrate(
+                any(),
+                any(),
+                any<VibrationEffect>(),
+                any(),
+                any<VibrationAttributes>(),
+            )
     }
 
     @Test
@@ -303,7 +325,14 @@
         assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
         assertThat(uiEventLoggerFake.eventId(0))
             .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED.id)
-        verify(vibratorHelper).vibrate(any<VibrationEffect>())
+        verify(vibratorHelper)
+            .vibrate(
+                any(),
+                any(),
+                any<VibrationEffect>(),
+                any(),
+                any<VibrationAttributes>(),
+            )
     }
 
     @Test
@@ -326,7 +355,14 @@
         // Event index 1 since initially displaying the triggered chip would also log an event.
         assertThat(uiEventLoggerFake.eventId(1))
             .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED.id)
-        verify(vibratorHelper, never()).vibrate(any<VibrationEffect>())
+        verify(vibratorHelper, never())
+            .vibrate(
+                any(),
+                any(),
+                any<VibrationEffect>(),
+                any(),
+                any<VibrationAttributes>(),
+            )
     }
 
     @Test
@@ -403,7 +439,14 @@
         // Event index 1 since initially displaying the triggered chip would also log an event.
         assertThat(uiEventLoggerFake.eventId(1))
             .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED.id)
-        verify(vibratorHelper, never()).vibrate(any<VibrationEffect>())
+        verify(vibratorHelper, never())
+            .vibrate(
+                any(),
+                any(),
+                any<VibrationEffect>(),
+                any(),
+                any<VibrationAttributes>(),
+            )
     }
 
     @Test
@@ -483,7 +526,14 @@
         // Event index 1 since initially displaying the triggered chip would also log an event.
         assertThat(uiEventLoggerFake.eventId(1))
             .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED.id)
-        verify(vibratorHelper).vibrate(any<VibrationEffect>())
+        verify(vibratorHelper)
+            .vibrate(
+                any(),
+                any(),
+                any<VibrationEffect>(),
+                any(),
+                any<VibrationAttributes>(),
+            )
     }
 
     @Test
@@ -511,7 +561,14 @@
         // Event index 1 since initially displaying the triggered chip would also log an event.
         assertThat(uiEventLoggerFake.eventId(1))
             .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED.id)
-        verify(vibratorHelper).vibrate(any<VibrationEffect>())
+        verify(vibratorHelper)
+            .vibrate(
+                any(),
+                any(),
+                any<VibrationEffect>(),
+                any(),
+                any<VibrationAttributes>(),
+            )
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 6e24941..d33271b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.temporarydisplay.chipbar
 
 import android.os.PowerManager
+import android.os.VibrationAttributes
 import android.os.VibrationEffect
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
@@ -461,7 +462,7 @@
     }
 
     @Test
-    fun displayView_vibrationEffect_doubleClickEffect() {
+    fun displayView_vibrationEffect_doubleClickEffectWithHardwareFeedback() {
         underTest.displayView(
             createChipbarInfo(
                 Icon.Resource(R.id.check_box, null),
@@ -471,7 +472,14 @@
             )
         )
 
-        verify(vibratorHelper).vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK))
+        verify(vibratorHelper)
+            .vibrate(
+                any(),
+                any(),
+                eq(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK)),
+                any(),
+                eq(VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK)),
+            )
     }
 
     /** Regression test for b/266119467. */
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index 65735f0..4aaf347 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -17,10 +17,13 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.keyguard.shared.model.AuthenticationFlags
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
 
 class FakeBiometricSettingsRepository : BiometricSettingsRepository {
 
@@ -50,9 +53,12 @@
     override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
         get() = _isFaceAuthSupportedInCurrentPosture
 
-    private val _isCurrentUserInLockdown = MutableStateFlow(false)
     override val isCurrentUserInLockdown: Flow<Boolean>
-        get() = _isCurrentUserInLockdown
+        get() = _authFlags.map { it.isInUserLockdown }
+
+    private val _authFlags = MutableStateFlow(AuthenticationFlags(0, 0))
+    override val authenticationFlags: Flow<AuthenticationFlags>
+        get() = _authFlags
 
     fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) {
         _isFingerprintEnrolled.value = isFingerprintEnrolled
@@ -66,6 +72,10 @@
         _isFingerprintEnabledByDevicePolicy.value = isFingerprintEnabledByDevicePolicy
     }
 
+    fun setAuthenticationFlags(value: AuthenticationFlags) {
+        _authFlags.value = value
+    }
+
     fun setFaceEnrolled(isFaceEnrolled: Boolean) {
         _isFaceEnrolled.value = isFaceEnrolled
     }
@@ -79,7 +89,22 @@
     }
 
     fun setIsUserInLockdown(value: Boolean) {
-        _isCurrentUserInLockdown.value = value
+        if (value) {
+            setAuthenticationFlags(
+                AuthenticationFlags(
+                    _authFlags.value.userId,
+                    _authFlags.value.flag or
+                        LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
+                )
+            )
+        } else {
+            setAuthenticationFlags(
+                AuthenticationFlags(
+                    _authFlags.value.userId,
+                    LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
+                )
+            )
+        }
     }
 
     fun setIsNonStrongBiometricAllowed(value: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBouncerMessageRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBouncerMessageRepository.kt
new file mode 100644
index 0000000..b03b4ba
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBouncerMessageRepository.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository
+import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class FakeBouncerMessageRepository : BouncerMessageRepository {
+    private val _primaryAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
+    override val primaryAuthMessage: StateFlow<BouncerMessageModel?>
+        get() = _primaryAuthMessage
+
+    private val _faceAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
+    override val faceAcquisitionMessage: StateFlow<BouncerMessageModel?>
+        get() = _faceAcquisitionMessage
+    private val _fingerprintAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
+    override val fingerprintAcquisitionMessage: StateFlow<BouncerMessageModel?>
+        get() = _fingerprintAcquisitionMessage
+    private val _customMessage = MutableStateFlow<BouncerMessageModel?>(null)
+    override val customMessage: StateFlow<BouncerMessageModel?>
+        get() = _customMessage
+    private val _biometricAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
+    override val biometricAuthMessage: StateFlow<BouncerMessageModel?>
+        get() = _biometricAuthMessage
+    private val _authFlagsMessage = MutableStateFlow<BouncerMessageModel?>(null)
+    override val authFlagsMessage: StateFlow<BouncerMessageModel?>
+        get() = _authFlagsMessage
+
+    private val _biometricLockedOutMessage = MutableStateFlow<BouncerMessageModel?>(null)
+    override val biometricLockedOutMessage: Flow<BouncerMessageModel?>
+        get() = _biometricLockedOutMessage
+
+    override fun setPrimaryAuthMessage(value: BouncerMessageModel?) {
+        _primaryAuthMessage.value = value
+    }
+
+    override fun setFaceAcquisitionMessage(value: BouncerMessageModel?) {
+        _faceAcquisitionMessage.value = value
+    }
+
+    override fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) {
+        _fingerprintAcquisitionMessage.value = value
+    }
+
+    override fun setCustomMessage(value: BouncerMessageModel?) {
+        _customMessage.value = value
+    }
+
+    fun setBiometricAuthMessage(value: BouncerMessageModel?) {
+        _biometricAuthMessage.value = value
+    }
+
+    fun setAuthFlagsMessage(value: BouncerMessageModel?) {
+        _authFlagsMessage.value = value
+    }
+
+    fun setBiometricLockedOutMessage(value: BouncerMessageModel?) {
+        _biometricLockedOutMessage.value = value
+    }
+
+    override fun clearMessage() {
+        _primaryAuthMessage.value = null
+        _faceAcquisitionMessage.value = null
+        _fingerprintAcquisitionMessage.value = null
+        _customMessage.value = null
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/FillRequestEventLogger.java b/services/autofill/java/com/android/server/autofill/FillRequestEventLogger.java
index 06a616c..994802d 100644
--- a/services/autofill/java/com/android/server/autofill/FillRequestEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/FillRequestEventLogger.java
@@ -74,9 +74,6 @@
     public static final int TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE =
             AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
 
-    // Augmented autofill currently doesn't have an assigned request_id, use -2 as the magic number.
-    public static final int AUGMENTED_AUTOFILL_REQUEST_ID = -2;
-
     private final int mSessionId;
     private Optional<FillRequestEventInternal> mEventInternal;
 
diff --git a/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java b/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
index 8f2ab71..fc5fb1a 100644
--- a/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
@@ -16,17 +16,20 @@
 
 package com.android.server.autofill;
 
+import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_ONLY;
+import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER;
+
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__DIALOG;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_FAILURE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_RESULT_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_SUCCESS;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__AUTHENTICATION_TYPE_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__DATASET_AUTHENTICATION;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__FULL_AUTHENTICATION;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__DIALOG;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_CANCELLED;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_FAILURE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_SESSION_DESTROYED;
@@ -218,7 +221,16 @@
 
   public void maybeSetAvailableCount(int val) {
     mEventInternal.ifPresent(event -> {
-      event.mAvailableCount = val;
+      // Don't reset if it's already populated.
+      // This is just a technical limitation of not having complicated logic.
+      // Autofill Provider may return some datasets which are applicable to data types.
+      // In such a case, we set available count to the number of datasets provided.
+      // However, it's possible that those data types aren't detected by PCC, so in effect, there
+      // are 0 datasets. In the codebase, we treat it as null response, which may call this again
+      // to set 0. But we don't want to overwrite this value.
+      if (event.mAvailableCount == 0) {
+        event.mAvailableCount = val;
+      }
     });
   }
 
@@ -318,6 +330,50 @@
     });
   }
 
+  /**
+   * Set available_pcc_count.
+   */
+  public void maybeSetAvailablePccCount(int val) {
+    mEventInternal.ifPresent(event -> {
+      event.mAvailablePccCount = val;
+    });
+  }
+
+  /**
+   * Set available_pcc_only_count.
+   */
+  public void maybeSetAvailablePccOnlyCount(int val) {
+    mEventInternal.ifPresent(event -> {
+      event.mAvailablePccOnlyCount = val;
+    });
+  }
+
+  /**
+   * Set available_pcc_count.
+   */
+  public void maybeSetAvailableDatasetsPccCount(@Nullable List<Dataset> datasetList) {
+    mEventInternal.ifPresent(event -> {
+      int pccOnlyCount = 0;
+      int pccCount = 0;
+      if (datasetList != null) {
+        for (int i = 0; i < datasetList.size(); i++) {
+          Dataset dataset = datasetList.get(i);
+          if (dataset != null) {
+            if (dataset.getEligibleReason() == PICK_REASON_PCC_DETECTION_ONLY) {
+              pccOnlyCount++;
+              pccCount++;
+            } else if (dataset.getEligibleReason()
+                    == PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER) {
+              pccCount++;
+            }
+          }
+        }
+      }
+      event.mAvailablePccOnlyCount = pccOnlyCount;
+      event.mAvailablePccCount = pccCount;
+    });
+  }
+
 
   /**
    * Log an AUTOFILL_FILL_RESPONSE_REPORTED event.
@@ -344,7 +400,9 @@
           + " mLatencyAuthenticationUiDisplayMillis=" + event.mLatencyAuthenticationUiDisplayMillis
           + " mLatencyDatasetDisplayMillis=" + event.mLatencyDatasetDisplayMillis
           + " mResponseStatus=" + event.mResponseStatus
-          + " mLatencyResponseProcessingMillis=" + event.mLatencyResponseProcessingMillis);
+          + " mLatencyResponseProcessingMillis=" + event.mLatencyResponseProcessingMillis
+          + " mAvailablePccCount=" + event.mAvailablePccCount
+          + " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount);
     }
     FrameworkStatsLog.write(
         AUTOFILL_FILL_RESPONSE_REPORTED,
@@ -361,7 +419,9 @@
         event.mLatencyAuthenticationUiDisplayMillis,
         event.mLatencyDatasetDisplayMillis,
         event.mResponseStatus,
-        event.mLatencyResponseProcessingMillis);
+        event.mLatencyResponseProcessingMillis,
+        event.mAvailablePccCount,
+        event.mAvailablePccOnlyCount);
     mEventInternal = Optional.empty();
   }
 
@@ -379,6 +439,8 @@
     int mLatencyDatasetDisplayMillis = 0;
     int mResponseStatus = RESPONSE_STATUS_UNKNOWN;
     int mLatencyResponseProcessingMillis = 0;
+    int mAvailablePccCount;
+    int mAvailablePccOnlyCount;
 
     FillResponseEventInternal() {
     }
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index ca743cb..b2f9a93 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -16,6 +16,8 @@
 
 package com.android.server.autofill;
 
+import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_ONLY;
+import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
@@ -46,6 +48,12 @@
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUSED_BEFORE_FILL_DIALOG_RESPONSE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_NO_PCC;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PCC_DETECTION_ONLY;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PROVIDER_DETECTION_ONLY;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_UNKNOWN;
 import static com.android.server.autofill.Helper.sVerbose;
 
 import android.annotation.IntDef;
@@ -116,6 +124,22 @@
     public @interface AuthenticationResult {
     }
 
+    /**
+     * Reasons why the picked dataset was present. These are wrappers around
+     * {@link com.android.os.AtomsProto.AutofillPresentationEventReported.DatasetPickedReason}.
+     * This enum is similar to {@link android.service.autofill.Dataset.DatasetEligibleReason}
+     */
+    @IntDef(prefix = {"PICK_REASON"}, value = {
+            PICK_REASON_UNKNOWN,
+            PICK_REASON_NO_PCC,
+            PICK_REASON_PROVIDER_DETECTION_ONLY,
+            PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC,
+            PICK_REASON_PCC_DETECTION_ONLY,
+            PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DatasetPickedReason {}
+
     public static final int NOT_SHOWN_REASON_ANY_SHOWN =
             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
     public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED =
@@ -151,6 +175,18 @@
     public static final int AUTHENTICATION_RESULT_FAILURE =
             AUTOFILL_PRESENTATION_EVENT_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_FAILURE;
 
+    public static final int PICK_REASON_UNKNOWN =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_UNKNOWN;
+    public static final int PICK_REASON_NO_PCC =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_NO_PCC;
+     public static final int PICK_REASON_PROVIDER_DETECTION_ONLY =
+             AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PROVIDER_DETECTION_ONLY;
+    public static final int PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC;
+    public static final int PICK_REASON_PCC_DETECTION_ONLY =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PCC_DETECTION_ONLY;
+    public static final int PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER;
     private final int mSessionId;
     private Optional<PresentationStatsEventInternal> mEventInternal;
 
@@ -194,36 +230,61 @@
     public void maybeSetAvailableCount(@Nullable List<Dataset> datasetList,
             AutofillId currentViewId) {
         mEventInternal.ifPresent(event -> {
-            int availableCount = getDatasetCountForAutofillId(datasetList, currentViewId);
-            event.mAvailableCount = availableCount;
-            event.mIsDatasetAvailable = availableCount > 0;
+            CountContainer container = getDatasetCountForAutofillId(datasetList, currentViewId);
+            event.mAvailableCount = container.mAvailableCount;
+            event.mAvailablePccCount = container.mAvailablePccCount;
+            event.mAvailablePccOnlyCount = container.mAvailablePccOnlyCount;
+            event.mIsDatasetAvailable = container.mAvailableCount > 0;
         });
     }
 
     public void maybeSetCountShown(@Nullable List<Dataset> datasetList,
             AutofillId currentViewId) {
         mEventInternal.ifPresent(event -> {
-            int countShown = getDatasetCountForAutofillId(datasetList, currentViewId);
-            event.mCountShown = countShown;
-            if (countShown > 0) {
+            CountContainer container = getDatasetCountForAutofillId(datasetList, currentViewId);
+            event.mCountShown = container.mAvailableCount;
+            if (container.mAvailableCount > 0) {
                 event.mNoPresentationReason = NOT_SHOWN_REASON_ANY_SHOWN;
             }
         });
     }
 
-    private static int getDatasetCountForAutofillId(@Nullable List<Dataset> datasetList,
+    private static CountContainer getDatasetCountForAutofillId(@Nullable List<Dataset> datasetList,
             AutofillId currentViewId) {
-        int availableCount = 0;
+
+        CountContainer container = new CountContainer();
         if (datasetList != null) {
             for (int i = 0; i < datasetList.size(); i++) {
                 Dataset data = datasetList.get(i);
                 if (data != null && data.getFieldIds() != null
                         && data.getFieldIds().contains(currentViewId)) {
-                    availableCount += 1;
+                    container.mAvailableCount += 1;
+                    if (data.getEligibleReason() == PICK_REASON_PCC_DETECTION_ONLY) {
+                        container.mAvailablePccOnlyCount++;
+                        container.mAvailablePccCount++;
+                    } else if (data.getEligibleReason()
+                            == PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER) {
+                        container.mAvailablePccCount++;
+                    }
                 }
             }
         }
-        return availableCount;
+        return container;
+    }
+
+    private static class CountContainer{
+        int mAvailableCount = 0;
+        int mAvailablePccCount = 0;
+        int mAvailablePccOnlyCount = 0;
+
+        CountContainer() {}
+
+        CountContainer(int availableCount, int availablePccCount,
+                int availablePccOnlyCount) {
+            mAvailableCount = availableCount;
+            mAvailablePccCount = availablePccCount;
+            mAvailablePccOnlyCount = availablePccOnlyCount;
+        }
     }
 
     public void maybeSetCountFilteredUserTyping(int countFilteredUserTyping) {
@@ -375,6 +436,46 @@
         });
     }
 
+    /**
+     * Set available_pcc_count.
+     */
+    public void maybeSetAvailablePccCount(int val) {
+        mEventInternal.ifPresent(event -> {
+            event.mAvailablePccCount = val;
+        });
+    }
+
+    /**
+     * Set available_pcc_only_count.
+     */
+    public void maybeSetAvailablePccOnlyCount(int val) {
+        mEventInternal.ifPresent(event -> {
+            event.mAvailablePccOnlyCount = val;
+        });
+    }
+
+    /**
+     * Set selected_dataset_picked_reason.
+     */
+    public void maybeSetSelectedDatasetPickReason(@Dataset.DatasetEligibleReason int val) {
+        mEventInternal.ifPresent(event -> {
+            event.mSelectedDatasetPickedReason = convertDatasetPickReason(val);
+        });
+    }
+
+    private int convertDatasetPickReason(@Dataset.DatasetEligibleReason int val) {
+        switch (val) {
+            case 0:
+            case 1:
+            case 2:
+            case 3:
+            case 4:
+            case 5:
+                return val;
+        }
+        return PICK_REASON_UNKNOWN;
+    }
+
 
     public void logAndEndEvent() {
         if (!mEventInternal.isPresent()) {
@@ -410,7 +511,10 @@
                     + " mAuthenticationResult=" + event.mAuthenticationResult
                     + " mLatencyAuthenticationUiDisplayMillis="
                     + event.mLatencyAuthenticationUiDisplayMillis
-                    + " mLatencyDatasetDisplayMillis=" + event.mLatencyDatasetDisplayMillis);
+                    + " mLatencyDatasetDisplayMillis=" + event.mLatencyDatasetDisplayMillis
+                    + " mAvailablePccCount=" + event.mAvailablePccCount
+                    + " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount
+                    + " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason);
         }
 
         // TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -443,7 +547,10 @@
                 event.mAuthenticationType,
                 event.mAuthenticationResult,
                 event.mLatencyAuthenticationUiDisplayMillis,
-                event.mLatencyDatasetDisplayMillis);
+                event.mLatencyDatasetDisplayMillis,
+                event.mAvailablePccCount,
+                event.mAvailablePccOnlyCount,
+                event.mSelectedDatasetPickedReason);
         mEventInternal = Optional.empty();
     }
 
@@ -472,6 +579,9 @@
         int mAuthenticationResult = AUTHENTICATION_RESULT_UNKNOWN;
         int mLatencyAuthenticationUiDisplayMillis = -1;
         int mLatencyDatasetDisplayMillis = -1;
+        int mAvailablePccCount = -1;
+        int mAvailablePccOnlyCount = -1;
+        @DatasetPickedReason int mSelectedDatasetPickedReason = PICK_REASON_UNKNOWN;
 
         PresentationStatsEventInternal() {}
     }
diff --git a/services/autofill/java/com/android/server/autofill/SaveEventLogger.java b/services/autofill/java/com/android/server/autofill/SaveEventLogger.java
index e5435c2..28e8e30 100644
--- a/services/autofill/java/com/android/server/autofill/SaveEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/SaveEventLogger.java
@@ -252,6 +252,15 @@
   }
 
   /**
+   * Set is_framework_created_save_info as long as mEventInternal presents.
+   */
+  public void maybeSetIsFrameworkCreatedSaveInfo(boolean val) {
+    mEventInternal.ifPresent(event -> {
+      event.mIsFrameworkCreatedSaveInfo = val;
+    });
+  }
+
+  /**
    * Log an AUTOFILL_SAVE_EVENT_REPORTED event.
    */
   public void logAndEndEvent() {
@@ -277,7 +286,8 @@
           + " mIsSaved=" + event.mIsSaved
           + " mLatencySaveUiDisplayMillis=" + event.mLatencySaveUiDisplayMillis
           + " mLatencySaveRequestMillis=" + event.mLatencySaveRequestMillis
-          + " mLatencySaveFinishMillis=" + event.mLatencySaveFinishMillis);
+          + " mLatencySaveFinishMillis=" + event.mLatencySaveFinishMillis
+          + " mIsFrameworkCreatedSaveInfo=" + event.mIsFrameworkCreatedSaveInfo);
     }
     FrameworkStatsLog.write(
         AUTOFILL_SAVE_EVENT_REPORTED,
@@ -295,7 +305,8 @@
         event.mIsSaved,
         event.mLatencySaveUiDisplayMillis,
         event.mLatencySaveRequestMillis,
-        event.mLatencySaveFinishMillis);
+        event.mLatencySaveFinishMillis,
+        event.mIsFrameworkCreatedSaveInfo);
     mEventInternal = Optional.empty();
   }
 
@@ -314,6 +325,7 @@
     long mLatencySaveUiDisplayMillis = 0;
     long mLatencySaveRequestMillis = 0;
     long mLatencySaveFinishMillis = 0;
+    boolean mIsFrameworkCreatedSaveInfo = false;
 
     SaveEventInternal() {
     }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 192fdfe..2d03daa 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -19,6 +19,12 @@
 import static android.Manifest.permission.PROVIDE_OWN_AUTOFILL_SUGGESTIONS;
 import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
 import static android.service.autofill.AutofillService.EXTRA_FILL_RESPONSE;
+import static android.service.autofill.Dataset.PICK_REASON_NO_PCC;
+import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_ONLY;
+import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER;
+import static android.service.autofill.Dataset.PICK_REASON_PROVIDER_DETECTION_ONLY;
+import static android.service.autofill.Dataset.PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC;
+import static android.service.autofill.Dataset.PICK_REASON_UNKNOWN;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
@@ -36,6 +42,7 @@
 import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
 import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
 import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
+import static android.view.autofill.AutofillManager.COMMIT_REASON_SESSION_DESTROYED;
 import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN;
 import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
@@ -79,7 +86,6 @@
 import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE;
 import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_TRIGGER_ID_SET;
 import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_UNKNOWN;
-import static com.android.server.autofill.SessionCommittedEventLogger.COMMIT_REASON_SESSION_DESTROYED;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
 
@@ -124,6 +130,7 @@
 import android.service.autofill.AutofillService;
 import android.service.autofill.CompositeUserData;
 import android.service.autofill.Dataset;
+import android.service.autofill.Dataset.DatasetEligibleReason;
 import android.service.autofill.FieldClassification;
 import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FieldClassificationUserData;
@@ -1158,7 +1165,7 @@
             }
             mSessionFlags.mAugmentedAutofillOnly = true;
             mFillRequestEventLogger.maybeSetRequestId(AUGMENTED_AUTOFILL_REQUEST_ID);
-            mFillRequestEventLogger.maybeSetIsAugmented(mSessionFlags.mAugmentedAutofillOnly);
+            mFillRequestEventLogger.maybeSetIsAugmented(true);
             mFillRequestEventLogger.logAndEndEvent();
             triggerAugmentedAutofillLocked(flags);
             return;
@@ -1554,9 +1561,8 @@
                 Slog.d(TAG, message.toString());
             }
         }
-
-        if (((response.getDatasets() == null || response.getDatasets().isEmpty())
-                        && response.getAuthentication() == null)
+        List<Dataset> datasetList = response.getDatasets();
+        if (((datasetList == null || datasetList.isEmpty()) && response.getAuthentication() == null)
                 || autofillDisabled) {
             // Response is "empty" from a UI point of view, need to notify client.
             notifyUnavailableToClient(
@@ -1578,6 +1584,9 @@
             }
         }
 
+        mFillResponseEventLogger.maybeSetAvailableCount(
+                datasetList == null ? 0 : datasetList.size());
+
         // TODO(b/266379948): Ideally wait for PCC request to finish for a while more
         // (say 100ms) before proceeding further on.
 
@@ -1728,6 +1737,7 @@
             }
             if (ids.isEmpty()) return saveInfo;
             AutofillId[] autofillIds = new AutofillId[ids.size()];
+            mSaveEventLogger.maybeSetIsFrameworkCreatedSaveInfo(true);
             ids.toArray(autofillIds);
             return SaveInfo.copy(saveInfo, autofillIds);
         }
@@ -1798,6 +1808,13 @@
 
     private void computeDatasetsForProviderAndUpdateContainer(
             FillResponse response, DatasetComputationContainer container) {
+        @DatasetEligibleReason int globalPickReason = PICK_REASON_UNKNOWN;
+        boolean isPccEnabled = mService.isPccClassificationEnabled();
+        if (isPccEnabled) {
+            globalPickReason = PICK_REASON_PROVIDER_DETECTION_ONLY;
+        } else {
+            globalPickReason = PICK_REASON_NO_PCC;
+        }
         List<Dataset> datasets = response.getDatasets();
         if (datasets == null) return;
         ArrayMap<AutofillId, Set<Dataset>> autofillIdToDatasetMap = new ArrayMap<>();
@@ -1805,6 +1822,7 @@
         Set<AutofillId> eligibleAutofillIds = new ArraySet<>();
         for (Dataset dataset : response.getDatasets()) {
             if (dataset.getFieldIds() == null || dataset.getFieldIds().isEmpty()) continue;
+            @DatasetEligibleReason int pickReason = globalPickReason;
             if (dataset.getAutofillDatatypes() != null
                     && !dataset.getAutofillDatatypes().isEmpty()) {
                 // This dataset has information relevant for detection too, so we should filter
@@ -1827,6 +1845,7 @@
                 if (newSize == 0) continue;
 
                 if (conversionRequired) {
+                    pickReason = PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC;
                     ArrayList<AutofillId> fieldIds = new ArrayList<>(newSize);
                     ArrayList<AutofillValue> fieldValues = new ArrayList<>(newSize);
                     ArrayList<RemoteViews> fieldPresentations = new ArrayList<>(newSize);
@@ -1870,6 +1889,7 @@
                                     dataset.getAuthentication());
                 }
             }
+            dataset.setEligibleReasonReason(pickReason);
             eligibleDatasets.add(dataset);
             for (AutofillId id : dataset.getFieldIds()) {
                 eligibleAutofillIds.add(id);
@@ -1905,6 +1925,8 @@
             Set<AutofillId> eligibleAutofillIds = new ArraySet<>();
 
             for (int i = 0; i < datasets.size(); i++) {
+
+                @DatasetEligibleReason int pickReason = PICK_REASON_PCC_DETECTION_ONLY;
                 Dataset dataset = datasets.get(i);
                 if (dataset.getAutofillDatatypes() == null
                         || dataset.getAutofillDatatypes().isEmpty()) continue;
@@ -1919,7 +1941,12 @@
                 Set<AutofillId> datasetAutofillIds = new ArraySet<>();
 
                 for (int j = 0; j < dataset.getAutofillDatatypes().size(); j++) {
-                    if (dataset.getAutofillDatatypes().get(j) == null) continue;
+                    if (dataset.getAutofillDatatypes().get(j) == null) {
+                        if (dataset.getFieldIds() != null && dataset.getFieldIds().get(j) != null) {
+                            pickReason = PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER;
+                        }
+                        continue;
+                    }
                     String hint = dataset.getAutofillDatatypes().get(j);
 
                     if (hintsToAutofillIdMap.containsKey(hint)) {
@@ -1966,6 +1993,7 @@
                                 null,
                                 dataset.getId(),
                                 dataset.getAuthentication());
+                dataset.setEligibleReasonReason(pickReason);
                 eligibleDatasets.add(newDataset);
                 Set<Dataset> newDatasets;
                 for (AutofillId autofillId : datasetAutofillIds) {
@@ -2229,7 +2257,6 @@
     @Override
     public void save() {
         synchronized (mLock) {
-            mSaveEventLogger.maybeSetSaveButtonClicked(true);
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#save() rejected - session: "
                         + id + " destroyed");
@@ -2248,7 +2275,6 @@
     public void cancelSave() {
         synchronized (mLock) {
             mSessionFlags.mShowingSaveUi = false;
-            mSaveEventLogger.maybeSetDialogDismissed(true);
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#cancelSave() rejected - session: "
                         + id + " destroyed");
@@ -2707,6 +2733,7 @@
         mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
                 Event.NO_SAVE_UI_REASON_NONE,
                 COMMIT_REASON_UNKNOWN));
+        logAllEvents(COMMIT_REASON_UNKNOWN);
     }
 
     /**
@@ -2720,6 +2747,7 @@
             @AutofillCommitReason int commitReason) {
         mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
                 saveDialogNotShowReason, commitReason));
+        logAllEvents(commitReason);
     }
 
     private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason,
@@ -2951,6 +2979,7 @@
                 changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
                 manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
                 mComponentName, mCompatMode, saveDialogNotShowReason);
+        logAllEvents(commitReason);
     }
 
     /**
@@ -3403,7 +3432,7 @@
                 getUiForShowing().showSaveUi(serviceLabel, serviceIcon,
                         mService.getServicePackageName(), saveInfo, this,
                         mComponentName, this, mContext,  mPendingSaveUi, isUpdate, mCompatMode,
-                        response.getShowSaveDialogIcon());
+                        response.getShowSaveDialogIcon(), mSaveEventLogger);
                 mSaveEventLogger.maybeSetLatencySaveUiDisplayMillis(
                     SystemClock.elapsedRealtime()- saveUiDisplayStartTimestamp);
                 if (client != null) {
@@ -4015,8 +4044,6 @@
                     mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
                     mPresentationStatsEventLogger.maybeSetAvailableCount(
                             response.getDatasets(), mCurrentViewId);
-                    mFillResponseEventLogger.maybeSetAvailableCount(
-                        response.getDatasets(), mCurrentViewId);
                 }
 
                 if (isSameViewEntered) {
@@ -4388,7 +4415,7 @@
 
         getUiForShowing().showFillDialog(filledId, response, filterText,
                 mService.getServicePackageName(), mComponentName, serviceIcon, this,
-                id, mCompatMode);
+                id, mCompatMode, mPresentationStatsEventLogger);
         return true;
     }
 
@@ -4707,6 +4734,7 @@
             autofillableIds = null;
         }
         // Log the existing FillResponse event.
+        mFillResponseEventLogger.maybeSetAvailableCount(0);
         mFillResponseEventLogger.logAndEndEvent();
         mService.resetLastResponse();
 
@@ -4818,6 +4846,7 @@
         mFillRequestEventLogger.maybeSetAppPackageUid(uid);
         mFillRequestEventLogger.maybeSetFlags(mFlags);
         mFillRequestEventLogger.maybeSetRequestId(AUGMENTED_AUTOFILL_REQUEST_ID);
+        mFillRequestEventLogger.maybeSetIsAugmented(true);
         mFillRequestEventLogger.logAndEndEvent();
 
         final ViewState viewState = mViewStates.get(mCurrentViewId);
@@ -4934,10 +4963,10 @@
         mResponses.put(requestId, newResponse);
         mClientState = newClientState != null ? newClientState : newResponse.getClientState();
 
-        mPresentationStatsEventLogger.maybeSetAvailableCount(
-                newResponse.getDatasets(), mCurrentViewId);
-        mFillResponseEventLogger.maybeSetAvailableCount(
-            newResponse.getDatasets(), mCurrentViewId);
+        List<Dataset> datasetList = newResponse.getDatasets();
+
+        mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId);
+        mFillResponseEventLogger.maybeSetAvailableDatasetsPccCount(datasetList);
 
         setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false);
         updateFillDialogTriggerIdsLocked();
@@ -5062,6 +5091,8 @@
                 if (generateEvent) {
                     mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType);
                     mPresentationStatsEventLogger.maybeSetSelectedDatasetId(datasetIndex);
+                    mPresentationStatsEventLogger.maybeSetSelectedDatasetPickReason(
+                            dataset.getEligibleReason());
                 }
                 if (mCurrentViewId != null) {
                     mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
@@ -5655,6 +5686,19 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void logAllEvents(@AutofillCommitReason int val) {
+        mSessionCommittedEventLogger.maybeSetCommitReason(val);
+        mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
+        mSessionCommittedEventLogger.maybeSetSessionDurationMillis(
+            SystemClock.elapsedRealtime() - mStartTime);
+        mFillRequestEventLogger.logAndEndEvent();
+        mFillResponseEventLogger.logAndEndEvent();
+        mPresentationStatsEventLogger.logAndEndEvent();
+        mSaveEventLogger.logAndEndEvent();
+        mSessionCommittedEventLogger.logAndEndEvent();
+    }
+
     /**
      * Destroy this session and perform any clean up work.
      *
@@ -5669,15 +5713,7 @@
     @GuardedBy("mLock")
     RemoteFillService destroyLocked() {
         // Log unlogged events.
-        mSessionCommittedEventLogger.maybeSetCommitReason(COMMIT_REASON_SESSION_DESTROYED);
-        mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
-        mSessionCommittedEventLogger.maybeSetSessionDurationMillis(
-            SystemClock.elapsedRealtime() - mStartTime);
-        mSessionCommittedEventLogger.logAndEndEvent();
-        mPresentationStatsEventLogger.logAndEndEvent();
-        mSaveEventLogger.logAndEndEvent();
-        mFillResponseEventLogger.logAndEndEvent();
-        mFillRequestEventLogger.logAndEndEvent();
+        logAllEvents(COMMIT_REASON_SESSION_DESTROYED);
 
         if (mDestroyed) {
             return null;
diff --git a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
index 541ec80..cd37073 100644
--- a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
@@ -16,13 +16,8 @@
 
 package com.android.server.autofill;
 
+import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_ACTIVITY_FINISHED;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_SESSION_DESTROYED;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_UNKNOWN;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CHANGED;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CLICKED;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_COMMITTED;
 import static com.android.server.autofill.Helper.sVerbose;
 
 import android.annotation.IntDef;
@@ -32,7 +27,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
-
+import android.view.autofill.AutofillManager.AutofillCommitReason;
 import com.android.internal.util.FrameworkStatsLog;
 
 import java.lang.annotation.Retention;
@@ -45,35 +40,6 @@
 public final class SessionCommittedEventLogger {
   private static final String TAG = "SessionCommittedEventLogger";
 
-  /**
-   * Reasons why presentation was not shown. These are wrappers around
-   * {@link com.android.os.AtomsProto.AutofillSessionCommitted.AutofillCommitReason}.
-   */
-  @IntDef(prefix = {"COMMIT_REASON"}, value = {
-      COMMIT_REASON_UNKNOWN,
-      COMMIT_REASON_ACTIVITY_FINISHED,
-      COMMIT_REASON_VIEW_COMMITTED,
-      COMMIT_REASON_VIEW_CLICKED,
-      COMMIT_REASON_VIEW_CHANGED,
-      COMMIT_REASON_SESSION_DESTROYED
-  })
-  @Retention(RetentionPolicy.SOURCE)
-  public @interface CommitReason {
-  }
-
-  public static final int COMMIT_REASON_UNKNOWN =
-      AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_UNKNOWN;
-  public static final int COMMIT_REASON_ACTIVITY_FINISHED =
-      AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_ACTIVITY_FINISHED;
-  public static final int COMMIT_REASON_VIEW_COMMITTED =
-      AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_COMMITTED;
-  public static final int COMMIT_REASON_VIEW_CLICKED =
-      AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CLICKED;
-  public static final int COMMIT_REASON_VIEW_CHANGED =
-      AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CHANGED;
-  public static final int COMMIT_REASON_SESSION_DESTROYED =
-      AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_SESSION_DESTROYED;
-
   private final int mSessionId;
   private Optional<SessionCommittedEventInternal> mEventInternal;
 
@@ -110,9 +76,9 @@
   /**
    * Set commit_reason as long as mEventInternal presents.
    */
-  public void maybeSetCommitReason(@CommitReason int val) {
+  public void maybeSetCommitReason(@AutofillCommitReason int val) {
     mEventInternal.ifPresent(event -> {
-      event.mCommitReason = val;
+        event.mCommitReason = val;
     });
   }
 
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index b3cbe45..f92d38d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -52,6 +52,8 @@
 import com.android.server.UiModeManagerInternal;
 import com.android.server.UiThread;
 import com.android.server.autofill.Helper;
+import com.android.server.autofill.PresentationStatsEventLogger;
+import com.android.server.autofill.SaveEventLogger;
 import com.android.server.utils.Slogf;
 
 import java.io.PrintWriter;
@@ -326,7 +328,7 @@
             @NonNull ValueFinder valueFinder, @NonNull ComponentName componentName,
             @NonNull AutoFillUiCallback callback, @NonNull Context context,
             @NonNull PendingUi pendingSaveUi, boolean isUpdate, boolean compatMode,
-            boolean showServiceIcon) {
+            boolean showServiceIcon, @Nullable SaveEventLogger mSaveEventLogger) {
         if (sVerbose) {
             Slogf.v(TAG, "showSaveUi(update=%b) for %s and display %d: %s", isUpdate,
                     componentName.toShortString(), context.getDisplayId(), info);
@@ -355,6 +357,9 @@
                 @Override
                 public void onSave() {
                     log.setType(MetricsEvent.TYPE_ACTION);
+                    if (mSaveEventLogger != null) {
+                        mSaveEventLogger.maybeSetSaveButtonClicked(true);
+                    }
                     hideSaveUiUiThread(callback);
                     callback.save();
                     destroySaveUiUiThread(pendingSaveUi, true);
@@ -363,6 +368,9 @@
                 @Override
                 public void onCancel(IntentSender listener) {
                     log.setType(MetricsEvent.TYPE_DISMISS);
+                    if (mSaveEventLogger != null) {
+                        mSaveEventLogger.maybeSetCancelButtonClicked(true);
+                    }
                     hideSaveUiUiThread(callback);
                     if (listener != null) {
                         try {
@@ -384,6 +392,9 @@
                         callback.cancelSave();
                     }
                     mMetricsLogger.write(log);
+                    if (mSaveEventLogger != null) {
+                        mSaveEventLogger.maybeSetDialogDismissed(true);
+                    }
                 }
 
                 @Override
@@ -400,7 +411,8 @@
     public void showFillDialog(@NonNull AutofillId focusedId, @NonNull FillResponse response,
             @Nullable String filterText, @Nullable String servicePackageName,
             @NonNull ComponentName componentName, @Nullable Drawable serviceIcon,
-            @NonNull AutoFillUiCallback callback, int sessionId, boolean compatMode) {
+            @NonNull AutoFillUiCallback callback, int sessionId, boolean compatMode,
+            @Nullable PresentationStatsEventLogger mPresentationStatsEventLogger) {
         if (sVerbose) {
             Slog.v(TAG, "showFillDialog for "
                     + componentName.toShortString() + ": " + response);
@@ -442,6 +454,10 @@
                         @Override
                         public void onDatasetPicked(Dataset dataset) {
                             log(MetricsEvent.TYPE_ACTION);
+                            if (mPresentationStatsEventLogger != null) {
+                                mPresentationStatsEventLogger.maybeSetPositiveCtaButtonClicked(
+                                    true);
+                            }
                             hideFillDialogUiThread(callback);
                             if (mCallback != null) {
                                 final int datasetIndex = response.getDatasets().indexOf(dataset);
@@ -453,6 +469,9 @@
                         @Override
                         public void onDismissed() {
                             log(MetricsEvent.TYPE_DISMISS);
+                            if (mPresentationStatsEventLogger != null) {
+                                mPresentationStatsEventLogger.maybeSetDialogDismissed(true);
+                            }
                             hideFillDialogUiThread(callback);
                             callback.requestShowSoftInput(focusedId);
                             callback.requestFallbackFromFillDialog();
@@ -461,6 +480,10 @@
                         @Override
                         public void onCanceled() {
                             log(MetricsEvent.TYPE_CLOSE);
+                            if (mPresentationStatsEventLogger != null) {
+                                mPresentationStatsEventLogger.maybeSetNegativeCtaButtonClicked(
+                                    true);
+                            }
                             hideFillDialogUiThread(callback);
                             callback.requestShowSoftInput(focusedId);
                             callback.requestFallbackFromFillDialog();
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceConfig.java b/services/companion/java/com/android/server/companion/CompanionDeviceConfig.java
index 8570515..a2b71e0 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceConfig.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceConfig.java
@@ -16,6 +16,7 @@
 
 package com.android.server.companion;
 
+import android.os.Binder;
 import android.provider.DeviceConfig;
 
 /**
@@ -34,7 +35,12 @@
      * Returns whether the given flag is currently enabled, with a default value of {@code false}.
      */
     public static boolean isEnabled(String flag) {
-        return DeviceConfig.getBoolean(NAMESPACE_COMPANION, flag, /* defaultValue= */ false);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return DeviceConfig.getBoolean(NAMESPACE_COMPANION, flag, /* defaultValue= */ false);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index ed61d64..6b99494 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -1383,10 +1383,11 @@
         }
 
         @Override
-        public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback) {
+        public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback,
+                @CrossDeviceSyncControllerCallback.Type int type) {
             if (CompanionDeviceConfig.isEnabled(
                     CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
-                mCrossDeviceSyncController.registerCallMetadataSyncCallback(callback);
+                mCrossDeviceSyncController.registerCallMetadataSyncCallback(callback, type);
             }
         }
 
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
index 3b108e6..c5ef4e4 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
@@ -36,7 +36,8 @@
      * Registers a callback from an InCallService / ConnectionService to CDM to process sync
      * requests and perform call control actions.
      */
-    void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback);
+    void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback,
+            @CrossDeviceSyncControllerCallback.Type int type);
 
     /**
      * Requests a sync from an InCallService / ConnectionService to CDM, for the given association
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java
index 459bf98..7371824 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java
@@ -17,6 +17,7 @@
 package com.android.server.companion.datatransfer.contextsync;
 
 import android.media.AudioManager;
+import android.net.Uri;
 import android.os.Bundle;
 import android.telecom.Call;
 import android.telecom.Connection;
@@ -62,20 +63,14 @@
                         final CallMetadataSyncConnection existingConnection =
                                 mActiveConnections.get(new CallMetadataSyncConnectionIdentifier(
                                         associationId, call.getId()));
-                        if (existingConnection == null) {
-                            final Bundle extras = new Bundle();
-                            extras.putInt(CrossDeviceSyncController.EXTRA_ASSOCIATION_ID,
-                                    associationId);
-                            extras.putParcelable(CrossDeviceSyncController.EXTRA_CALL, call);
-                            mTelecomManager.addNewIncomingCall(call.getPhoneAccountHandle(),
-                                    extras);
-                        } else {
+                        if (existingConnection != null) {
                             existingConnection.update(call);
                         }
                     }
                     // Remove obsolete calls.
                     mActiveConnections.values().removeIf(connection -> {
-                        if (!callMetadataSyncData.hasCall(connection.getCallId())) {
+                        if (associationId == connection.getAssociationId()
+                                && !callMetadataSyncData.hasCall(connection.getCallId())) {
                             connection.setDisconnected(new DisconnectCause(DisconnectCause.REMOTE));
                             return true;
                         }
@@ -91,7 +86,8 @@
         mAudioManager = getSystemService(AudioManager.class);
         mTelecomManager = getSystemService(TelecomManager.class);
         mCdmsi = LocalServices.getService(CompanionDeviceManagerServiceInternal.class);
-        mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback);
+        mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback,
+                CrossDeviceSyncControllerCallback.TYPE_CONNECTION_SERVICE);
     }
 
     @Override
@@ -101,6 +97,11 @@
                 CrossDeviceSyncController.EXTRA_ASSOCIATION_ID);
         final CallMetadataSyncData.Call call = connectionRequest.getExtras().getParcelable(
                 CrossDeviceSyncController.EXTRA_CALL, CallMetadataSyncData.Call.class);
+        // InCallServices outside of framework (like Dialer's) might try to read this, and crash
+        // when they can't. Remove it once we're done with it, as well as the other internal ones.
+        connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_CALL);
+        connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_CALL_FACILITATOR_ID);
+        connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_ASSOCIATION_ID);
         final CallMetadataSyncConnection connection = new CallMetadataSyncConnection(
                 mTelecomManager,
                 mAudioManager,
@@ -113,15 +114,17 @@
                                 CrossDeviceSyncController.createCallControlMessage(callId, action));
                     }
                 });
-        connection.setConnectionProperties(
-                Connection.PROPERTY_IS_EXTERNAL_CALL | Connection.PROPERTY_SELF_MANAGED);
+        connection.setConnectionProperties(Connection.PROPERTY_IS_EXTERNAL_CALL);
+        connection.setInitializing();
         return connection;
     }
 
     @Override
     public void onCreateIncomingConnectionFailed(PhoneAccountHandle phoneAccountHandle,
             ConnectionRequest connectionRequest) {
-        Slog.e(TAG, "onCreateIncomingConnectionFailed for: " + phoneAccountHandle.getId());
+        final String id =
+                phoneAccountHandle != null ? phoneAccountHandle.getId() : "unknown PhoneAccount";
+        Slog.e(TAG, "onCreateOutgoingConnectionFailed for: " + id);
     }
 
     @Override
@@ -132,7 +135,6 @@
         final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
         call.setId(UUID.randomUUID().toString());
         call.setStatus(android.companion.Telecom.Call.UNKNOWN_STATUS);
-        call.setPhoneAccountHandle(phoneAccountHandle);
         final CallMetadataSyncData.CallFacilitator callFacilitator =
                 new CallMetadataSyncData.CallFacilitator(phoneAccount.getLabel().toString(),
                         phoneAccount.getExtras().getString(
@@ -142,6 +144,10 @@
         final int associationId = connectionRequest.getExtras().getInt(
                 CrossDeviceSyncController.EXTRA_ASSOCIATION_ID);
 
+        connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_CALL);
+        connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_CALL_FACILITATOR_ID);
+        connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_ASSOCIATION_ID);
+
         final CallMetadataSyncConnection connection = new CallMetadataSyncConnection(
                 mTelecomManager,
                 mAudioManager,
@@ -154,8 +160,7 @@
                                 CrossDeviceSyncController.createCallControlMessage(callId, action));
                     }
                 });
-        connection.setConnectionProperties(
-                Connection.PROPERTY_IS_EXTERNAL_CALL | Connection.PROPERTY_SELF_MANAGED);
+        connection.setConnectionProperties(Connection.PROPERTY_IS_EXTERNAL_CALL);
 
         mCdmsi.sendCrossDeviceSyncMessage(associationId,
                 CrossDeviceSyncController.createCallCreateMessage(call.getId(),
@@ -168,13 +173,21 @@
     @Override
     public void onCreateOutgoingConnectionFailed(PhoneAccountHandle phoneAccountHandle,
             ConnectionRequest connectionRequest) {
-        Slog.e(TAG, "onCreateIncomingConnectionFailed for: " + phoneAccountHandle.getId());
+        final String id =
+                phoneAccountHandle != null ? phoneAccountHandle.getId() : "unknown PhoneAccount";
+        Slog.e(TAG, "onCreateOutgoingConnectionFailed for: " + id);
     }
 
     @Override
     public void onCreateConnectionComplete(Connection connection) {
         if (connection instanceof CallMetadataSyncConnection) {
-            ((CallMetadataSyncConnection) connection).initialize();
+            final CallMetadataSyncConnection callMetadataSyncConnection =
+                    (CallMetadataSyncConnection) connection;
+            callMetadataSyncConnection.initialize();
+            mActiveConnections.put(new CallMetadataSyncConnectionIdentifier(
+                            callMetadataSyncConnection.getAssociationId(),
+                            callMetadataSyncConnection.getCallId()),
+                    callMetadataSyncConnection);
         }
     }
 
@@ -242,7 +255,11 @@
             return mCall.getId();
         }
 
-        public void initialize() {
+        public int getAssociationId() {
+            return mAssociationId;
+        }
+
+        private void initialize() {
             final int status = mCall.getStatus();
             if (status == android.companion.Telecom.Call.RINGING_SILENCED) {
                 mTelecomManager.silenceRinger();
@@ -254,12 +271,21 @@
                 setActive();
             } else if (state == Call.STATE_HOLDING) {
                 setOnHold();
+            } else if (state == Call.STATE_DISCONNECTED) {
+                setDisconnected(new DisconnectCause(DisconnectCause.REMOTE));
             } else {
-                Slog.e(TAG, "Could not initialize call to unknown state");
+                setInitialized();
+            }
+
+            final String callerId = mCall.getCallerId();
+            if (callerId != null) {
+                setCallerDisplayName(callerId, TelecomManager.PRESENTATION_ALLOWED);
+                setAddress(Uri.fromParts("custom", mCall.getCallerId(), null),
+                        TelecomManager.PRESENTATION_ALLOWED);
             }
 
             final Bundle extras = new Bundle();
-            extras.putString(CrossDeviceCall.EXTRA_CALL_ID, mCall.getId());
+            extras.putString(CrossDeviceSyncController.EXTRA_CALL_ID, mCall.getId());
             putExtras(extras);
 
             int capabilities = getConnectionCapabilities();
@@ -280,7 +306,7 @@
             }
         }
 
-        public void update(CallMetadataSyncData.Call call) {
+        private void update(CallMetadataSyncData.Call call) {
             final int status = call.getStatus();
             if (status == android.companion.Telecom.Call.RINGING_SILENCED
                     && mCall.getStatus() != android.companion.Telecom.Call.RINGING_SILENCED) {
@@ -295,31 +321,29 @@
                     setActive();
                 } else if (state == Call.STATE_HOLDING) {
                     setOnHold();
+                } else if (state == Call.STATE_DISCONNECTED) {
+                    setDisconnected(new DisconnectCause(DisconnectCause.REMOTE));
                 } else {
                     Slog.e(TAG, "Could not update call to unknown state");
                 }
             }
 
             int capabilities = getConnectionCapabilities();
+            mCall.setControls(call.getControls());
             final boolean hasHoldControl = mCall.hasControl(
                     android.companion.Telecom.PUT_ON_HOLD)
                     || mCall.hasControl(android.companion.Telecom.TAKE_OFF_HOLD);
-            if (hasHoldControl != ((getConnectionCapabilities() & CAPABILITY_HOLD)
-                    == CAPABILITY_HOLD)) {
-                if (hasHoldControl) {
-                    capabilities |= CAPABILITY_HOLD;
-                } else {
-                    capabilities &= ~CAPABILITY_HOLD;
-                }
+            if (hasHoldControl) {
+                capabilities |= CAPABILITY_HOLD;
+            } else {
+                capabilities &= ~CAPABILITY_HOLD;
             }
-            final boolean hasMuteControl = mCall.hasControl(android.companion.Telecom.MUTE);
-            if (hasMuteControl != ((getConnectionCapabilities() & CAPABILITY_MUTE)
-                    == CAPABILITY_MUTE)) {
-                if (hasMuteControl) {
-                    capabilities |= CAPABILITY_MUTE;
-                } else {
-                    capabilities &= ~CAPABILITY_MUTE;
-                }
+            final boolean hasMuteControl = mCall.hasControl(android.companion.Telecom.MUTE)
+                    || mCall.hasControl(android.companion.Telecom.UNMUTE);
+            if (hasMuteControl) {
+                capabilities |= CAPABILITY_MUTE;
+            } else {
+                capabilities &= ~CAPABILITY_MUTE;
             }
             mAudioManager.setMicrophoneMute(
                     mCall.hasControl(android.companion.Telecom.UNMUTE));
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java
index b3cf772..d8621cb 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java
@@ -20,7 +20,6 @@
 import android.companion.ContextSyncMessage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telecom.PhoneAccountHandle;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -189,7 +188,6 @@
         private String mCallerId;
         private byte[] mAppIcon;
         private CallFacilitator mFacilitator;
-        private PhoneAccountHandle mPhoneAccountHandle;
         private int mStatus;
         private final Set<Integer> mControls = new HashSet<>();
 
@@ -200,9 +198,6 @@
             call.setAppIcon(parcel.readBlob());
             call.setFacilitator(parcel.readParcelable(CallFacilitator.class.getClassLoader(),
                     CallFacilitator.class));
-            call.setPhoneAccountHandle(
-                    parcel.readParcelable(PhoneAccountHandle.class.getClassLoader(),
-                            android.telecom.PhoneAccountHandle.class));
             call.setStatus(parcel.readInt());
             final int numberOfControls = parcel.readInt();
             for (int i = 0; i < numberOfControls; i++) {
@@ -217,7 +212,6 @@
             parcel.writeString(mCallerId);
             parcel.writeBlob(mAppIcon);
             parcel.writeParcelable(mFacilitator, parcelableFlags);
-            parcel.writeParcelable(mPhoneAccountHandle, parcelableFlags);
             parcel.writeInt(mStatus);
             parcel.writeInt(mControls.size());
             for (int control : mControls) {
@@ -241,10 +235,6 @@
             mFacilitator = facilitator;
         }
 
-        void setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
-            mPhoneAccountHandle = phoneAccountHandle;
-        }
-
         void setStatus(int status) {
             mStatus = status;
         }
@@ -253,6 +243,11 @@
             mControls.add(control);
         }
 
+        void setControls(Set<Integer> controls) {
+            mControls.clear();
+            mControls.addAll(controls);
+        }
+
         String getId() {
             return mId;
         }
@@ -269,10 +264,6 @@
             return mFacilitator;
         }
 
-        PhoneAccountHandle getPhoneAccountHandle() {
-            return mPhoneAccountHandle;
-        }
-
         int getStatus() {
             return mStatus;
         }
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
index 1f5e168..b46d5d3 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
@@ -79,16 +79,15 @@
                         int callControlAction) {
                     final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId,
                             mCurrentCalls.values());
+                    if (crossDeviceCall == null) {
+                        return;
+                    }
                     switch (callControlAction) {
                         case android.companion.Telecom.ACCEPT:
-                            if (crossDeviceCall != null) {
-                                crossDeviceCall.doAccept();
-                            }
+                            crossDeviceCall.doAccept();
                             break;
                         case android.companion.Telecom.REJECT:
-                            if (crossDeviceCall != null) {
-                                crossDeviceCall.doReject();
-                            }
+                            crossDeviceCall.doReject();
                             break;
                         case android.companion.Telecom.SILENCE:
                             doSilence();
@@ -100,19 +99,13 @@
                             doUnmute();
                             break;
                         case android.companion.Telecom.END:
-                            if (crossDeviceCall != null) {
-                                crossDeviceCall.doEnd();
-                            }
+                            crossDeviceCall.doEnd();
                             break;
                         case android.companion.Telecom.PUT_ON_HOLD:
-                            if (crossDeviceCall != null) {
-                                crossDeviceCall.doPutOnHold();
-                            }
+                            crossDeviceCall.doPutOnHold();
                             break;
                         case android.companion.Telecom.TAKE_OFF_HOLD:
-                            if (crossDeviceCall != null) {
-                                crossDeviceCall.doTakeOffHold();
-                            }
+                            crossDeviceCall.doTakeOffHold();
                             break;
                         default:
                     }
@@ -148,7 +141,8 @@
         super.onCreate();
         if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
             mCdmsi = LocalServices.getService(CompanionDeviceManagerServiceInternal.class);
-            mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback);
+            mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback,
+                    CrossDeviceSyncControllerCallback.TYPE_IN_CALL_SERVICE);
         }
     }
 
@@ -156,7 +150,7 @@
         if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
                 && mNumberOfActiveSyncAssociations > 0) {
             mCurrentCalls.putAll(getCalls().stream().collect(Collectors.toMap(call -> call,
-                    call -> new CrossDeviceCall(getPackageManager(), call, getCallAudioState()))));
+                    call -> new CrossDeviceCall(this, call, getCallAudioState()))));
             mCurrentCalls.keySet().forEach(call -> call.registerCallback(mTelecomCallback,
                     getMainThreadHandler()));
             sync(getUserId());
@@ -182,7 +176,7 @@
         if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
                 && mNumberOfActiveSyncAssociations > 0) {
             mCurrentCalls.put(call,
-                    new CrossDeviceCall(getPackageManager(), call, getCallAudioState()));
+                    new CrossDeviceCall(this, call, getCallAudioState()));
             call.registerCallback(mTelecomCallback);
             sync(getUserId());
         }
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
index de7bf40..fec6923 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
@@ -17,10 +17,15 @@
 package com.android.server.companion.datatransfer.contextsync;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.net.Uri;
 import android.telecom.Call;
 import android.telecom.CallAudioState;
+import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.util.Slog;
 
@@ -35,40 +40,51 @@
 
     private static final String TAG = "CrossDeviceCall";
 
-    public static final String EXTRA_CALL_ID =
-            "com.android.companion.datatransfer.contextsync.extra.CALL_ID";
-
     private final String mId;
-    private Call mCall;
+    private final Call mCall;
     @VisibleForTesting boolean mIsEnterprise;
-    @VisibleForTesting boolean mIsOtt;
     private final String mCallingAppPackageName;
     private String mCallingAppName;
     private byte[] mCallingAppIcon;
     private String mCallerDisplayName;
+    private int mCallerDisplayNamePresentation;
     private int mStatus = android.companion.Telecom.Call.UNKNOWN_STATUS;
     private String mContactDisplayName;
+    private Uri mHandle;
+    private int mHandlePresentation;
     private boolean mIsMuted;
     private final Set<Integer> mControls = new HashSet<>();
+    private final boolean mIsCallPlacedByContextSync;
 
-    public CrossDeviceCall(PackageManager packageManager, @NonNull Call call,
+    public CrossDeviceCall(Context context, @NonNull Call call,
             CallAudioState callAudioState) {
-        this(packageManager, call.getDetails(), callAudioState);
-        mCall = call;
-        call.putExtra(EXTRA_CALL_ID, mId);
+        this(context, call, call.getDetails(), callAudioState);
     }
 
-    CrossDeviceCall(PackageManager packageManager, Call.Details callDetails,
+    CrossDeviceCall(Context context, Call.Details callDetails,
             CallAudioState callAudioState) {
+        this(context, /* call= */ null, callDetails, callAudioState);
+    }
+
+    private CrossDeviceCall(Context context, @Nullable Call call,
+            Call.Details callDetails, CallAudioState callAudioState) {
+        mCall = call;
         final String predefinedId = callDetails.getIntentExtras() != null
-                ? callDetails.getIntentExtras().getString(EXTRA_CALL_ID) : null;
-        mId = predefinedId != null ? predefinedId : UUID.randomUUID().toString();
+                ? callDetails.getIntentExtras().getString(CrossDeviceSyncController.EXTRA_CALL_ID)
+                : null;
+        final String generatedId = UUID.randomUUID().toString();
+        mId = predefinedId != null ? (generatedId + predefinedId) : generatedId;
+        if (call != null) {
+            call.putExtra(CrossDeviceSyncController.EXTRA_CALL_ID, mId);
+        }
+        mIsCallPlacedByContextSync =
+                new ComponentName(context, CallMetadataSyncConnectionService.class)
+                        .equals(callDetails.getAccountHandle().getComponentName());
         mCallingAppPackageName =
                 callDetails.getAccountHandle().getComponentName().getPackageName();
-        mIsOtt = (callDetails.getCallCapabilities() & Call.Details.PROPERTY_SELF_MANAGED)
-                == Call.Details.PROPERTY_SELF_MANAGED;
         mIsEnterprise = (callDetails.getCallProperties() & Call.Details.PROPERTY_ENTERPRISE_CALL)
                 == Call.Details.PROPERTY_ENTERPRISE_CALL;
+        final PackageManager packageManager = context.getPackageManager();
         try {
             final ApplicationInfo applicationInfo = packageManager
                     .getApplicationInfo(mCallingAppPackageName,
@@ -108,7 +124,10 @@
     @VisibleForTesting
     void updateCallDetails(Call.Details callDetails) {
         mCallerDisplayName = callDetails.getCallerDisplayName();
+        mCallerDisplayNamePresentation = callDetails.getCallerDisplayNamePresentation();
         mContactDisplayName = callDetails.getContactDisplayName();
+        mHandle = callDetails.getHandle();
+        mHandlePresentation = callDetails.getHandlePresentation();
         mStatus = convertStateToStatus(callDetails.getState());
         mControls.clear();
         if (mStatus == android.companion.Telecom.Call.RINGING
@@ -145,7 +164,14 @@
                 return android.companion.Telecom.Call.ONGOING;
             case Call.STATE_RINGING:
                 return android.companion.Telecom.Call.RINGING;
+            case Call.STATE_AUDIO_PROCESSING:
+                return android.companion.Telecom.Call.AUDIO_PROCESSING;
+            case Call.STATE_SIMULATED_RINGING:
+                return android.companion.Telecom.Call.RINGING_SIMULATED;
+            case Call.STATE_DISCONNECTED:
+                return android.companion.Telecom.Call.DISCONNECTED;
             default:
+                Slog.e(TAG, "Couldn't resolve state to status: " + callState);
                 return android.companion.Telecom.Call.UNKNOWN_STATUS;
         }
     }
@@ -163,6 +189,12 @@
             case android.companion.Telecom.Call.RINGING:
             case android.companion.Telecom.Call.RINGING_SILENCED:
                 return Call.STATE_RINGING;
+            case android.companion.Telecom.Call.AUDIO_PROCESSING:
+                return Call.STATE_AUDIO_PROCESSING;
+            case android.companion.Telecom.Call.RINGING_SIMULATED:
+                return Call.STATE_SIMULATED_RINGING;
+            case android.companion.Telecom.Call.DISCONNECTED:
+                return Call.STATE_DISCONNECTED;
             case android.companion.Telecom.Call.UNKNOWN_STATUS:
             default:
                 return Call.STATE_NEW;
@@ -195,10 +227,23 @@
      * @param isAdminBlocked whether there is an admin that has blocked contacts over Bluetooth
      */
     public String getReadableCallerId(boolean isAdminBlocked) {
-        if (mIsOtt) {
+        if (mIsEnterprise && isAdminBlocked) {
+            // Cannot use any contact information.
+            return getNonContactString();
+        }
+        return mContactDisplayName != null ? mContactDisplayName : getNonContactString();
+    }
+
+    private String getNonContactString() {
+        if (mCallerDisplayName != null
+                && mCallerDisplayNamePresentation == TelecomManager.PRESENTATION_ALLOWED) {
             return mCallerDisplayName;
         }
-        return mIsEnterprise && isAdminBlocked ? mCallerDisplayName : mContactDisplayName;
+        if (mHandle != null && mHandle.getSchemeSpecificPart() != null
+                && mHandlePresentation == TelecomManager.PRESENTATION_ALLOWED) {
+            return mHandle.getSchemeSpecificPart();
+        }
+        return null;
     }
 
     public int getStatus() {
@@ -209,6 +254,10 @@
         return mControls;
     }
 
+    public boolean isCallPlacedByContextSync() {
+        return mIsCallPlacedByContextSync;
+    }
+
     void doAccept() {
         mCall.answer(VideoProfile.STATE_AUDIO_ONLY);
     }
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java
index 8c6ff86..bf82f3f 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java
@@ -20,6 +20,7 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.companion.AssociationInfo;
+import android.companion.CompanionDeviceManager;
 import android.companion.ContextSyncMessage;
 import android.companion.IOnMessageReceivedListener;
 import android.companion.IOnTransportsChangedListener;
@@ -44,8 +45,10 @@
 import com.android.server.companion.transport.CompanionTransportManager;
 
 import java.io.IOException;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -54,6 +57,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
+import java.util.stream.Collectors;
 
 /**
  * Monitors connections and sending / receiving of synced data.
@@ -62,6 +66,13 @@
 
     private static final String TAG = "CrossDeviceSyncController";
 
+    public static final String EXTRA_CALL_ID =
+            "com.android.companion.datatransfer.contextsync.extra.CALL_ID";
+    static final String EXTRA_FACILITATOR_ICON =
+            "com.android.companion.datatransfer.contextsync.extra.FACILITATOR_ICON";
+    static final String EXTRA_IS_REMOTE_ORIGIN =
+            "com.android.companion.datatransfer.contextsync.extra.IS_REMOTE_ORIGIN";
+
     static final String EXTRA_ASSOCIATION_ID =
             "com.android.server.companion.datatransfer.contextsync.extra.ASSOCIATION_ID";
     static final String EXTRA_CALL =
@@ -78,11 +89,13 @@
     private final Context mContext;
     private final CompanionTransportManager mCompanionTransportManager;
     private final PhoneAccountManager mPhoneAccountManager;
+    private final CallManager mCallManager;
     private final List<AssociationInfo> mConnectedAssociations = new ArrayList<>();
     private final Set<Integer> mBlocklist = new HashSet<>();
     private final List<CallMetadataSyncData.CallFacilitator> mCallFacilitators = new ArrayList<>();
 
-    private CrossDeviceSyncControllerCallback mCrossDeviceSyncControllerCallback;
+    private WeakReference<CrossDeviceSyncControllerCallback> mInCallServiceCallbackRef;
+    private WeakReference<CrossDeviceSyncControllerCallback> mConnectionServiceCallbackRef;
 
     public CrossDeviceSyncController(Context context,
             CompanionTransportManager companionTransportManager) {
@@ -104,25 +117,77 @@
                         mConnectedAssociations);
                 mConnectedAssociations.clear();
                 mConnectedAssociations.addAll(newAssociations);
-                if (mCrossDeviceSyncControllerCallback == null) {
-                    Slog.w(TAG, "No callback to report transports changed");
-                    return;
-                }
                 for (AssociationInfo associationInfo : newAssociations) {
-                    if (!existingAssociations.contains(associationInfo)
-                            && !isAssociationBlocked(associationInfo.getId())) {
-                        mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
-                                associationInfo.getUserId(), /* added= */ true);
-                        mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
+                    if (!existingAssociations.contains(associationInfo)) {
+                        // New association.
+                        if (!isAssociationBlocked(associationInfo)) {
+                            final CrossDeviceSyncControllerCallback callback =
+                                    mInCallServiceCallbackRef != null
+                                            ? mInCallServiceCallbackRef.get() : null;
+                            if (callback != null) {
+                                callback.updateNumberOfActiveSyncAssociations(
+                                        associationInfo.getUserId(), /* added= */ true);
+                                callback.requestCrossDeviceSync(associationInfo);
+                            } else {
+                                Slog.w(TAG, "No callback to report new transport");
+                                syncMessageToDevice(associationInfo.getId(),
+                                        createFacilitatorMessage());
+                            }
+                        } else {
+                            mBlocklist.add(associationInfo.getId());
+                            Slog.i(TAG, "New association was blocked from context syncing");
+                        }
                     }
                 }
                 for (AssociationInfo associationInfo : existingAssociations) {
                     if (!newAssociations.contains(associationInfo)) {
-                        if (isAssociationBlocked(associationInfo.getId())) {
-                            mBlocklist.remove(associationInfo.getId());
-                        } else {
-                            mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
-                                    associationInfo.getUserId(), /* added= */ false);
+                        // Removed association!
+                        mBlocklist.remove(associationInfo.getId());
+                        if (!isAssociationBlockedLocal(associationInfo.getId())) {
+                            final CrossDeviceSyncControllerCallback callback =
+                                    mInCallServiceCallbackRef != null
+                                            ? mInCallServiceCallbackRef.get() : null;
+                            if (callback != null) {
+                                callback.updateNumberOfActiveSyncAssociations(
+                                        associationInfo.getUserId(), /* added= */ false);
+                            } else {
+                                Slog.w(TAG, "No callback to report removed transport");
+                            }
+                        }
+                    } else {
+                        // Stable association!
+                        final boolean systemBlocked = isAssociationBlocked(associationInfo);
+                        if (isAssociationBlockedLocal(associationInfo.getId()) != systemBlocked) {
+                            // Block state has changed.
+                            final CrossDeviceSyncControllerCallback callback =
+                                    mInCallServiceCallbackRef != null
+                                            ? mInCallServiceCallbackRef.get() : null;
+                            if (!systemBlocked) {
+                                Slog.i(TAG, "Unblocking existing association for context sync");
+                                mBlocklist.remove(associationInfo.getId());
+                                if (callback != null) {
+                                    callback.updateNumberOfActiveSyncAssociations(
+                                            associationInfo.getUserId(), /* added= */ true);
+                                    callback.requestCrossDeviceSync(associationInfo);
+                                } else {
+                                    Slog.w(TAG, "No callback to report changed transport");
+                                    syncMessageToDevice(associationInfo.getId(),
+                                            createFacilitatorMessage());
+                                }
+                            } else {
+                                Slog.i(TAG, "Blocking existing association for context sync");
+                                mBlocklist.add(associationInfo.getId());
+                                if (callback != null) {
+                                    callback.updateNumberOfActiveSyncAssociations(
+                                            associationInfo.getUserId(), /* added= */ false);
+                                } else {
+                                    Slog.w(TAG, "No callback to report changed transport");
+                                }
+                                // Send empty message to device to clear its data (otherwise it
+                                // will get stale)
+                                syncMessageToDevice(associationInfo.getId(),
+                                        createEmptyMessage());
+                            }
                         }
                     }
                 }
@@ -132,18 +197,48 @@
                 new IOnMessageReceivedListener.Stub() {
                     @Override
                     public void onMessageReceived(int associationId, byte[] data) {
+                        if (isAssociationBlockedLocal(associationId)) {
+                            return;
+                        }
                         final CallMetadataSyncData processedData = processTelecomDataFromSync(data);
                         mPhoneAccountManager.updateFacilitators(associationId, processedData);
-                        processCallCreateRequests(associationId, processedData);
-                        if (mCrossDeviceSyncControllerCallback == null) {
+                        mCallManager.updateCalls(associationId, processedData);
+                        processCallCreateRequests(processedData);
+                        if (mInCallServiceCallbackRef == null
+                                && mConnectionServiceCallbackRef == null) {
                             Slog.w(TAG, "No callback to process context sync message");
                             return;
                         }
-                        mCrossDeviceSyncControllerCallback.processContextSyncMessage(associationId,
-                                processedData);
+                        final CrossDeviceSyncControllerCallback inCallServiceCallback =
+                                mInCallServiceCallbackRef != null ? mInCallServiceCallbackRef.get()
+                                        : null;
+                        if (inCallServiceCallback != null) {
+                            inCallServiceCallback.processContextSyncMessage(associationId,
+                                    processedData);
+                        } else {
+                            // This is dead; get rid of it lazily
+                            mInCallServiceCallbackRef = null;
+                        }
+
+                        final CrossDeviceSyncControllerCallback connectionServiceCallback =
+                                mConnectionServiceCallbackRef != null
+                                        ? mConnectionServiceCallbackRef.get() : null;
+                        if (connectionServiceCallback != null) {
+                            connectionServiceCallback.processContextSyncMessage(associationId,
+                                    processedData);
+                        } else {
+                            // This is dead; get rid of it lazily
+                            mConnectionServiceCallbackRef = null;
+                        }
                     }
                 });
         mPhoneAccountManager = new PhoneAccountManager(mContext);
+        mCallManager = new CallManager(mContext, mPhoneAccountManager);
+    }
+
+    private static boolean isAssociationBlocked(AssociationInfo info) {
+        return (info.getSystemDataSyncFlags() & CompanionDeviceManager.FLAG_CALL_METADATA)
+                != CompanionDeviceManager.FLAG_CALL_METADATA;
     }
 
     /** Invoke set-up tasks that happen when boot is completed. */
@@ -155,7 +250,7 @@
         mPhoneAccountManager.onBootCompleted();
 
         final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
-        if (telecomManager.getCallCapablePhoneAccounts().size() != 0) {
+        if (telecomManager != null && telecomManager.getCallCapablePhoneAccounts().size() != 0) {
             final PhoneAccountHandle defaultOutgoingTelAccountHandle =
                     telecomManager.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
             if (defaultOutgoingTelAccountHandle != null) {
@@ -171,8 +266,7 @@
         }
     }
 
-    private void processCallCreateRequests(int associationId,
-            CallMetadataSyncData callMetadataSyncData) {
+    private void processCallCreateRequests(CallMetadataSyncData callMetadataSyncData) {
         final Iterator<CallMetadataSyncData.CallCreateRequest> iterator =
                 callMetadataSyncData.getCallCreateRequests().iterator();
         while (iterator.hasNext()) {
@@ -184,7 +278,7 @@
                     final Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL,
                             request.getAddress().replaceAll("\\D+", ""), /* fragment= */ null);
                     final Bundle extras = new Bundle();
-                    extras.putString(CrossDeviceCall.EXTRA_CALL_ID, request.getId());
+                    extras.putString(EXTRA_CALL_ID, request.getId());
                     final Bundle outerExtras = new Bundle();
                     outerExtras.putParcelable(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
                     mContext.getSystemService(TelecomManager.class).placeCall(uri, outerExtras);
@@ -196,39 +290,33 @@
         }
     }
 
-    private boolean isAssociationBlocked(int associationId) {
+    /**
+     * This keeps track of "previous" state to calculate deltas. Use {@link #isAssociationBlocked}
+     * for all other use cases.
+     */
+    private boolean isAssociationBlockedLocal(int associationId) {
         return mBlocklist.contains(associationId);
     }
 
     /** Registers the call metadata callback. */
-    public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback) {
-        mCrossDeviceSyncControllerCallback = callback;
-        for (AssociationInfo associationInfo : mConnectedAssociations) {
-            if (!isAssociationBlocked(associationInfo.getId())) {
-                mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
-                        associationInfo.getUserId(), /* added= */ true);
-                mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
+    public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback,
+            @CrossDeviceSyncControllerCallback.Type int type) {
+        if (type == CrossDeviceSyncControllerCallback.TYPE_IN_CALL_SERVICE) {
+            mInCallServiceCallbackRef = new WeakReference<>(callback);
+            for (AssociationInfo associationInfo : mConnectedAssociations) {
+                if (!isAssociationBlocked(associationInfo)) {
+                    mBlocklist.remove(associationInfo.getId());
+                    callback.updateNumberOfActiveSyncAssociations(associationInfo.getUserId(),
+                            /* added= */ true);
+                    callback.requestCrossDeviceSync(associationInfo);
+                } else {
+                    mBlocklist.add(associationInfo.getId());
+                }
             }
-        }
-    }
-
-    /** Allow specific associated devices to enable / disable syncing. */
-    public void setSyncEnabled(AssociationInfo associationInfo, boolean enabled) {
-        if (enabled) {
-            if (isAssociationBlocked(associationInfo.getId())) {
-                mBlocklist.remove(associationInfo.getId());
-                mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
-                        associationInfo.getUserId(), /* added= */ true);
-                mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
-            }
+        } else if (type == CrossDeviceSyncControllerCallback.TYPE_CONNECTION_SERVICE) {
+            mConnectionServiceCallbackRef = new WeakReference<>(callback);
         } else {
-            if (!isAssociationBlocked(associationInfo.getId())) {
-                mBlocklist.add(associationInfo.getId());
-                mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
-                        associationInfo.getUserId(), /* added= */ false);
-                // Send empty message to device to clear its data (otherwise it will get stale)
-                syncMessageToDevice(associationInfo.getId(), createEmptyMessage());
-            }
+            Slog.e(TAG, "Cannot register callback of unknown type: " + type);
         }
     }
 
@@ -246,8 +334,7 @@
     public void syncToAllDevicesForUserId(int userId, Collection<CrossDeviceCall> calls) {
         final Set<Integer> associationIds = new HashSet<>();
         for (AssociationInfo associationInfo : mConnectedAssociations) {
-            if (associationInfo.getUserId() == userId && !isAssociationBlocked(
-                    associationInfo.getId())) {
+            if (associationInfo.getUserId() == userId && !isAssociationBlocked(associationInfo)) {
                 associationIds.add(associationInfo.getId());
             }
         }
@@ -269,7 +356,7 @@
      */
     public void syncToSingleDevice(AssociationInfo associationInfo,
             Collection<CrossDeviceCall> calls) {
-        if (isAssociationBlocked(associationInfo.getId())) {
+        if (isAssociationBlocked(associationInfo)) {
             Slog.e(TAG, "Cannot sync to requested device; connection is blocked");
             return;
         }
@@ -286,7 +373,7 @@
      * @param message         The message to sync.
      */
     public void syncMessageToDevice(int associationId, byte[] message) {
-        if (isAssociationBlocked(associationId)) {
+        if (isAssociationBlockedLocal(associationId)) {
             Slog.e(TAG, "Cannot sync to requested device; connection is blocked");
             return;
         }
@@ -491,6 +578,10 @@
         pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION);
         final long telecomToken = pos.start(ContextSyncMessage.TELECOM);
         for (CrossDeviceCall call : calls) {
+            if (call.isCallPlacedByContextSync()) {
+                // Do not sync any calls which our "ours" as that would be duplicative.
+                continue;
+            }
             final long callsToken = pos.start(Telecom.CALLS);
             pos.write(Telecom.Call.ID, call.getId());
             final long originToken = pos.start(Telecom.Call.ORIGIN);
@@ -559,6 +650,50 @@
         return pos.getBytes();
     }
 
+    /** Create a facilitator-only message, used before any calls are available as a call intake. */
+    private byte[] createFacilitatorMessage() {
+        return createCallUpdateMessage(Collections.emptyList(), -1);
+    }
+
+    @VisibleForTesting
+    static class CallManager {
+
+        @VisibleForTesting final Map<Integer, Set<String>> mCallIds = new HashMap<>();
+        private final TelecomManager mTelecomManager;
+        private final PhoneAccountManager mPhoneAccountManager;
+
+        CallManager(Context context, PhoneAccountManager phoneAccountManager) {
+            mTelecomManager = context.getSystemService(TelecomManager.class);
+            mPhoneAccountManager = phoneAccountManager;
+        }
+
+        /** Add any new calls to Telecom. The ConnectionService will handle everything else. */
+        void updateCalls(int associationId, CallMetadataSyncData data) {
+            final Set<String> oldCallIds = mCallIds.getOrDefault(associationId, new HashSet<>());
+            final Set<String> newCallIds = data.getCalls().stream().map(
+                    CallMetadataSyncData.Call::getId).collect(Collectors.toSet());
+            if (oldCallIds.equals(newCallIds)) {
+                return;
+            }
+
+            for (CallMetadataSyncData.Call currentCall : data.getCalls()) {
+                if (!oldCallIds.contains(currentCall.getId())
+                        && currentCall.getFacilitator() != null) {
+                    final Bundle extras = new Bundle();
+                    extras.putInt(EXTRA_ASSOCIATION_ID, associationId);
+                    extras.putBoolean(EXTRA_IS_REMOTE_ORIGIN, true);
+                    extras.putParcelable(EXTRA_CALL, currentCall);
+                    extras.putString(EXTRA_CALL_ID, currentCall.getId());
+                    extras.putByteArray(EXTRA_FACILITATOR_ICON, currentCall.getAppIcon());
+                    final PhoneAccountHandle handle = mPhoneAccountManager.getPhoneAccountHandle(
+                            associationId, currentCall.getFacilitator().getIdentifier());
+                    mTelecomManager.addNewIncomingCall(handle, extras);
+                }
+            }
+            mCallIds.put(associationId, newCallIds);
+        }
+    }
+
     static class PhoneAccountManager {
         private final Map<PhoneAccountHandleIdentifier, PhoneAccountHandle> mPhoneAccountHandles =
                 new HashMap<>();
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java
index 31e10a8..8a0ba27 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java
@@ -16,11 +16,25 @@
 
 package com.android.server.companion.datatransfer.contextsync;
 
+import android.annotation.IntDef;
 import android.companion.AssociationInfo;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /** Callback for call metadata syncing. */
 public abstract class CrossDeviceSyncControllerCallback {
 
+    static final int TYPE_CONNECTION_SERVICE = 1;
+    static final int TYPE_IN_CALL_SERVICE = 2;
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_CONNECTION_SERVICE,
+            TYPE_IN_CALL_SERVICE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {
+    }
+
     void processContextSyncMessage(int associationId, CallMetadataSyncData callMetadataSyncData) {}
 
     void requestCrossDeviceSync(AssociationInfo associationInfo) {}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 392b5df..4562b8f 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -262,8 +262,7 @@
     private static final boolean DEBUG_DELAYED_SERVICE = DEBUG_SERVICE;
     private static final boolean DEBUG_DELAYED_STARTS = DEBUG_DELAYED_SERVICE;
 
-    // STOPSHIP(b/260012573) turn it off.
-    private static final boolean DEBUG_SHORT_SERVICE = true; // DEBUG_SERVICE;
+    private static final boolean DEBUG_SHORT_SERVICE = DEBUG_SERVICE;
 
     private static final boolean LOG_SERVICE_START_STOP = DEBUG_SERVICE;
 
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index c78229b..eb7fa10 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -342,16 +342,20 @@
         return supportedStates;
     }
 
+    /**
+     * Returns the current {@link DeviceStateInfo} of the device. If there has been no base state
+     * or committed state provided, {@link DeviceStateManager#INVALID_DEVICE_STATE} will be returned
+     * respectively. The supported states will always be included.
+     *
+     */
+    @GuardedBy("mLock")
     @NonNull
     private DeviceStateInfo getDeviceStateInfoLocked() {
-        if (!mBaseState.isPresent() || !mCommittedState.isPresent()) {
-            throw new IllegalStateException("Trying to get the current DeviceStateInfo before the"
-                    + " initial state has been committed.");
-        }
-
         final int[] supportedStates = getSupportedStateIdentifiersLocked();
-        final int baseState = mBaseState.get().getIdentifier();
-        final int currentState = mCommittedState.get().getIdentifier();
+        final int baseState =
+                mBaseState.isPresent() ? mBaseState.get().getIdentifier() : INVALID_DEVICE_STATE;
+        final int currentState = mCommittedState.isPresent() ? mCommittedState.get().getIdentifier()
+                : INVALID_DEVICE_STATE;
 
         return new DeviceStateInfo(supportedStates, baseState, currentState);
     }
@@ -715,6 +719,9 @@
             }
             mProcessRecords.put(pid, record);
 
+            // Callback clients should not be notified of invalid device states, so calls to
+            // #getDeviceStateInfoLocked should be gated on checks if a committed state is present
+            // before getting the device state info.
             DeviceStateInfo currentInfo = mCommittedState.isPresent()
                     ? getDeviceStateInfoLocked() : null;
             if (currentInfo != null) {
@@ -1128,6 +1135,7 @@
 
     /** Implementation of {@link IDeviceStateManager} published as a binder service. */
     private final class BinderService extends IDeviceStateManager.Stub {
+
         @Override // Binder call
         public DeviceStateInfo getDeviceStateInfo() {
             synchronized (mLock) {
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 22b6a53..e8c65ef 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -316,7 +316,9 @@
     }
 
     /**
-     * Notify the BrightnessTracker that the user has changed the brightness of the display.
+     * Notify the BrightnessTracker that the brightness of the display has changed.
+     * We pass both the user change and system changes, so that we know the starting point
+     * of the next user interaction. Only user interactions are then sent as BrightnessChangeEvents.
      */
     public void notifyBrightnessChanged(float brightness, boolean userInitiated,
             float powerBrightnessFactor, boolean wasShortTermModelActive,
@@ -352,10 +354,8 @@
                 // Not currently gathering brightness change information
                 return;
             }
-
             float previousBrightness = mLastBrightness;
             mLastBrightness = brightness;
-
             if (!userInitiated) {
                 // We want to record what current brightness is so that we know what the user
                 // changed it from, but if it wasn't user initiated then we don't want to record it
@@ -429,7 +429,7 @@
 
         BrightnessChangeEvent event = builder.build();
         if (DEBUG) {
-            Slog.d(TAG, "Event " + event.brightness + " " + event.packageName);
+            Slog.d(TAG, "Event: " + event.toString());
         }
         synchronized (mEventsLock) {
             mEventsDirty = true;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 0861cb5..9d31572 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1908,21 +1908,8 @@
                 }
             }
 
-            // Report brightness to brightnesstracker:
-            // If brightness is not temporary (ie the slider has been released)
-            // AND if we are not in idle screen brightness mode.
-            if (!brightnessIsTemporary
-                    && (mAutomaticBrightnessController != null
-                    && !mAutomaticBrightnessController.isInIdleMode())) {
-                if (userInitiatedChange && (mAutomaticBrightnessController == null
-                        || !mAutomaticBrightnessController.hasValidAmbientLux())) {
-                    // If we don't have a valid lux reading we can't report a valid
-                    // slider event so notify as if the system changed the brightness.
-                    userInitiatedChange = false;
-                }
-                notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
-                        wasShortTermModelActive);
-            }
+            notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
+                    wasShortTermModelActive, autoBrightnessEnabled, brightnessIsTemporary);
 
             // We save the brightness info *after* the brightness setting has been changed and
             // adjustments made so that the brightness info reflects the latest value.
@@ -2758,22 +2745,43 @@
     }
 
     private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
-            boolean wasShortTermModelActive) {
+            boolean wasShortTermModelActive, boolean autobrightnessEnabled,
+            boolean brightnessIsTemporary) {
         final float brightnessInNits = convertToAdjustedNits(brightness);
-        if (mUseAutoBrightness && brightnessInNits >= 0.0f
-                && mAutomaticBrightnessController != null && mBrightnessTracker != null) {
-            // We only want to track changes on devices that can actually map the display backlight
-            // values into a physical brightness unit since the value provided by the API is in
-            // nits and not using the arbitrary backlight units.
-            final float powerFactor = mPowerRequest.lowPowerMode
-                    ? mPowerRequest.screenLowPowerBrightnessFactor
-                    : 1.0f;
-            mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
-                    powerFactor, wasShortTermModelActive,
-                    mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId,
-                    mAutomaticBrightnessController.getLastSensorValues(),
-                    mAutomaticBrightnessController.getLastSensorTimestamps());
+
+        // Don't report brightness to brightnessTracker:
+        // If brightness is temporary (ie the slider has not been released)
+        // or if we are in idle screen brightness mode.
+        // or display is not on
+        // or we shouldn't be using autobrightness
+        // or the nits is invalid.
+        if (brightnessIsTemporary
+                || mAutomaticBrightnessController == null
+                || mAutomaticBrightnessController.isInIdleMode()
+                || !autobrightnessEnabled
+                || mBrightnessTracker == null
+                || !mUseAutoBrightness
+                || brightnessInNits < 0.0f) {
+            return;
         }
+
+        if (userInitiated && !mAutomaticBrightnessController.hasValidAmbientLux()) {
+            // If we don't have a valid lux reading we can't report a valid
+            // slider event so notify as if the system changed the brightness.
+            userInitiated = false;
+        }
+
+        // We only want to track changes on devices that can actually map the display backlight
+        // values into a physical brightness unit since the value provided by the API is in
+        // nits and not using the arbitrary backlight units.
+        final float powerFactor = mPowerRequest.lowPowerMode
+                ? mPowerRequest.screenLowPowerBrightnessFactor
+                : 1.0f;
+        mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
+                powerFactor, wasShortTermModelActive,
+                mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId,
+                mAutomaticBrightnessController.getLastSensorValues(),
+                mAutomaticBrightnessController.getLastSensorTimestamps());
     }
 
     private float convertToNits(float brightness) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 3b3d5da..1674141 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -1454,7 +1454,7 @@
         // Skip the animation when the screen is off or suspended.
         boolean brightnessAdjusted = false;
         final boolean brightnessIsTemporary =
-                (mBrightnessReason.getReason() == BrightnessReason.REASON_TEMPORARY)
+                (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY)
                         || mAutomaticBrightnessStrategy
                         .isTemporaryAutoBrightnessAdjustmentApplied();
         if (!mPendingScreenOff) {
@@ -1539,21 +1539,9 @@
                 }
             }
 
-            // Report brightness to brightnesstracker:
-            // If brightness is not temporary (ie the slider has been released)
-            // AND if we are not in idle screen brightness mode.
-            if (!brightnessIsTemporary
-                    && (mAutomaticBrightnessController != null
-                    && !mAutomaticBrightnessController.isInIdleMode())) {
-                if (userInitiatedChange && (mAutomaticBrightnessController == null
-                        || !mAutomaticBrightnessController.hasValidAmbientLux())) {
-                    // If we don't have a valid lux reading we can't report a valid
-                    // slider event so notify as if the system changed the brightness.
-                    userInitiatedChange = false;
-                }
-                notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
-                        wasShortTermModelActive);
-            }
+            notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
+                    wasShortTermModelActive, mAutomaticBrightnessStrategy.isAutoBrightnessEnabled(),
+                    brightnessIsTemporary);
 
             // We save the brightness info *after* the brightness setting has been changed and
             // adjustments made so that the brightness info reflects the latest value.
@@ -2171,11 +2159,6 @@
         }, mClock.uptimeMillis());
     }
 
-    private float getAutoBrightnessAdjustmentSetting() {
-        final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT);
-        return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
-    }
 
     @Override
     public float getScreenBrightnessSetting() {
@@ -2215,23 +2198,45 @@
     }
 
     private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
-            boolean wasShortTermModelActive) {
+            boolean wasShortTermModelActive, boolean autobrightnessEnabled,
+            boolean brightnessIsTemporary) {
+
         final float brightnessInNits =
                 mDisplayBrightnessController.convertToAdjustedNits(brightness);
-        if (mAutomaticBrightnessStrategy.shouldUseAutoBrightness() && brightnessInNits >= 0.0f
-                && mAutomaticBrightnessController != null && mBrightnessTracker != null) {
-            // We only want to track changes on devices that can actually map the display backlight
-            // values into a physical brightness unit since the value provided by the API is in
-            // nits and not using the arbitrary backlight units.
-            final float powerFactor = mPowerRequest.lowPowerMode
-                    ? mPowerRequest.screenLowPowerBrightnessFactor
-                    : 1.0f;
-            mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
-                    powerFactor, wasShortTermModelActive,
-                    mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId,
-                    mAutomaticBrightnessController.getLastSensorValues(),
-                    mAutomaticBrightnessController.getLastSensorTimestamps());
+        // Don't report brightness to brightnessTracker:
+        // If brightness is temporary (ie the slider has not been released)
+        // or if we are in idle screen brightness mode.
+        // or display is not on
+        // or we shouldn't be using autobrightness
+        // or the nits is invalid.
+        if (brightnessIsTemporary
+                || mAutomaticBrightnessController == null
+                || mAutomaticBrightnessController.isInIdleMode()
+                || !autobrightnessEnabled
+                || mBrightnessTracker == null
+                || !mAutomaticBrightnessStrategy.shouldUseAutoBrightness()
+                || brightnessInNits < 0.0f) {
+            return;
         }
+
+        if (userInitiated && (mAutomaticBrightnessController == null
+                || !mAutomaticBrightnessController.hasValidAmbientLux())) {
+            // If we don't have a valid lux reading we can't report a valid
+            // slider event so notify as if the system changed the brightness.
+            userInitiated = false;
+        }
+
+        // We only want to track changes on devices that can actually map the display backlight
+        // values into a physical brightness unit since the value provided by the API is in
+        // nits and not using the arbitrary backlight units.
+        final float powerFactor = mPowerRequest.lowPowerMode
+                ? mPowerRequest.screenLowPowerBrightnessFactor
+                : 1.0f;
+        mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
+                powerFactor, wasShortTermModelActive,
+                mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId,
+                mAutomaticBrightnessController.getLastSensorValues(),
+                mAutomaticBrightnessController.getLastSensorTimestamps());
     }
 
     @Override
@@ -2426,9 +2431,6 @@
         }
     }
 
-    private static float clampAutoBrightnessAdjustment(float value) {
-        return MathUtils.constrain(value, -1.0f, 1.0f);
-    }
 
     private void noteScreenState(int screenState) {
         // Log screen state change with display id
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
index 169cc4a..3fae224 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
@@ -42,6 +42,13 @@
     }
 
     /**
+     * Clamps the brightness value in the maximum and the minimum brightness adjustment range
+     */
+    public static float clampBrightnessAdjustment(float value) {
+        return MathUtils.constrain(value, -1.0f, 1.0f);
+    }
+
+    /**
      * A utility to construct the DisplayBrightnessState
      */
     public static DisplayBrightnessState constructDisplayBrightnessState(
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 95cbf98..0e885dc 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -202,7 +202,7 @@
         final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT);
         mPendingAutoBrightnessAdjustment = Float.isNaN(adj) ? Float.NaN
-                : BrightnessUtils.clampAbsoluteBrightness(adj);
+                : BrightnessUtils.clampBrightnessAdjustment(adj);
         if (userSwitch) {
             processPendingAutoBrightnessAdjustments();
         }
@@ -402,6 +402,6 @@
     private float getAutoBrightnessAdjustmentSetting() {
         final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT);
-        return Float.isNaN(adj) ? 0.0f : BrightnessUtils.clampAbsoluteBrightness(adj);
+        return Float.isNaN(adj) ? 0.0f : BrightnessUtils.clampBrightnessAdjustment(adj);
     }
 }
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 182aa6f..93f6ff3 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -42,13 +42,10 @@
 
 import java.util.Objects;
 
-
 /* package */ final class AudioPoliciesDeviceRouteController implements DeviceRouteController {
 
     private static final String TAG = "APDeviceRoutesController";
 
-    private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
-
     @NonNull
     private final Context mContext;
     @NonNull
@@ -182,10 +179,12 @@
 
         synchronized (this) {
             return new MediaRoute2Info.Builder(
-                    DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
-                    .setVolumeHandling(mAudioManager.isVolumeFixed()
-                            ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
-                            : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+                            MediaRoute2Info.ROUTE_ID_DEVICE,
+                            mContext.getResources().getText(name).toString())
+                    .setVolumeHandling(
+                            mAudioManager.isVolumeFixed()
+                                    ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
+                                    : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                     .setVolume(mDeviceVolume)
                     .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
                     .setType(type)
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index cac22a6..b79991e 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -28,6 +28,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.content.BroadcastReceiver;
@@ -75,8 +76,10 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
@@ -97,6 +100,17 @@
     private static final String KEY_SCANNING_PACKAGE_MINIMUM_IMPORTANCE =
             "scanning_package_minimum_importance";
 
+    /**
+     * Contains the list of bluetooth permissions that are required to do system routing.
+     *
+     * <p>Alternatively, apps that hold {@link android.Manifest.permission#MODIFY_AUDIO_ROUTING} are
+     * also allowed to do system routing.
+     */
+    private static final String[] BLUETOOTH_PERMISSIONS_FOR_SYSTEM_ROUTING =
+            new String[] {
+                Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN
+            };
+
     private static int sPackageImportanceForScanning = DeviceConfig.getInt(
             MEDIA_BETTER_TOGETHER_NAMESPACE,
             /* name */ KEY_SCANNING_PACKAGE_MINIMUM_IMPORTANCE,
@@ -142,6 +156,7 @@
         }
     };
 
+    @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
     /* package */ MediaRouter2ServiceImpl(Context context) {
         mContext = context;
         mActivityManager = mContext.getSystemService(ActivityManager.class);
@@ -155,12 +170,28 @@
         screenOnOffIntentFilter.addAction(ACTION_SCREEN_OFF);
 
         mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter);
+        mContext.getPackageManager().addOnPermissionsChangeListener(this::onPermissionsChanged);
 
         DeviceConfig.addOnPropertiesChangedListener(MEDIA_BETTER_TOGETHER_NAMESPACE,
                 ActivityThread.currentApplication().getMainExecutor(),
                 this::onDeviceConfigChange);
     }
 
+    /**
+     * Called when there's a change in the permissions of an app.
+     *
+     * @param uid The uid of the app whose permissions changed.
+     */
+    private void onPermissionsChanged(int uid) {
+        synchronized (mLock) {
+            Optional<RouterRecord> affectedRouter =
+                    mAllRouterRecords.values().stream().filter(it -> it.mUid == uid).findFirst();
+            if (affectedRouter.isPresent()) {
+                affectedRouter.get().maybeUpdateSystemRoutingPermissionLocked();
+            }
+        }
+    }
+
     // Start of methods that implement MediaRouter2 operations.
 
     @NonNull
@@ -1511,6 +1542,7 @@
         public final int mPid;
         public final boolean mHasConfigureWifiDisplayPermission;
         public final boolean mHasModifyAudioRoutingPermission;
+        public final AtomicBoolean mHasBluetoothRoutingPermission;
         public final int mRouterId;
 
         public RouteDiscoveryPreference mDiscoveryPreference;
@@ -1528,15 +1560,47 @@
             mPid = pid;
             mHasConfigureWifiDisplayPermission = hasConfigureWifiDisplayPermission;
             mHasModifyAudioRoutingPermission = hasModifyAudioRoutingPermission;
+            mHasBluetoothRoutingPermission = new AtomicBoolean(fetchBluetoothPermission());
             mRouterId = mNextRouterOrManagerId.getAndIncrement();
         }
 
+        private boolean fetchBluetoothPermission() {
+            boolean hasBluetoothRoutingPermission = true;
+            for (String permission : BLUETOOTH_PERMISSIONS_FOR_SYSTEM_ROUTING) {
+                hasBluetoothRoutingPermission &=
+                        mContext.checkPermission(permission, mPid, mUid)
+                                == PackageManager.PERMISSION_GRANTED;
+            }
+            return hasBluetoothRoutingPermission;
+        }
+
         /**
          * Returns whether the corresponding router has permission to query and control system
          * routes.
          */
         public boolean hasSystemRoutingPermission() {
-            return mHasModifyAudioRoutingPermission;
+            return mHasModifyAudioRoutingPermission || mHasBluetoothRoutingPermission.get();
+        }
+
+        public void maybeUpdateSystemRoutingPermissionLocked() {
+            boolean oldSystemRoutingPermissionValue = hasSystemRoutingPermission();
+            mHasBluetoothRoutingPermission.set(fetchBluetoothPermission());
+            boolean newSystemRoutingPermissionValue = hasSystemRoutingPermission();
+            if (oldSystemRoutingPermissionValue != newSystemRoutingPermissionValue) {
+                Map<String, MediaRoute2Info> routesToReport =
+                        newSystemRoutingPermissionValue
+                                ? mUserRecord.mHandler.mLastNotifiedRoutesToPrivilegedRouters
+                                : mUserRecord.mHandler.mLastNotifiedRoutesToNonPrivilegedRouters;
+                notifyRoutesUpdated(routesToReport.values().stream().toList());
+
+                List<RoutingSessionInfo> sessionInfos =
+                        mUserRecord.mHandler.mSystemProvider.getSessionInfos();
+                RoutingSessionInfo systemSessionToReport =
+                        newSystemRoutingPermissionValue && !sessionInfos.isEmpty()
+                                ? sessionInfos.get(0)
+                                : mUserRecord.mHandler.mSystemProvider.getDefaultSessionInfo();
+                notifySessionInfoChanged(systemSessionToReport);
+            }
         }
 
         public void dispose() {
@@ -1559,6 +1623,14 @@
             pw.println(indent + "mPid=" + mPid);
             pw.println(indent + "mHasConfigureWifiDisplayPermission="
                     + mHasConfigureWifiDisplayPermission);
+            pw.println(
+                    indent
+                            + "mHasModifyAudioRoutingPermission="
+                            + mHasModifyAudioRoutingPermission);
+            pw.println(
+                    indent
+                            + "mHasBluetoothRoutingPermission="
+                            + mHasBluetoothRoutingPermission.get());
             pw.println(indent + "hasSystemRoutingPermission=" + hasSystemRoutingPermission());
             pw.println(indent + "mRouterId=" + mRouterId);
 
@@ -1581,6 +1653,19 @@
         }
 
         /**
+         * Sends the corresponding router an update for the given session.
+         *
+         * <p>Note: These updates are not directly visible to the app.
+         */
+        public void notifySessionInfoChanged(RoutingSessionInfo sessionInfo) {
+            try {
+                mRouter.notifySessionInfoChanged(sessionInfo);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to notify session info changed. Router probably died.", ex);
+            }
+        }
+
+        /**
          * Returns a filtered copy of {@code routes} that contains only the routes that are {@link
          * MediaRoute2Info#isVisibleTo visible} to the router corresponding to this record.
          */
@@ -2471,11 +2556,7 @@
                 @NonNull List<RouterRecord> routerRecords,
                 @NonNull RoutingSessionInfo sessionInfo) {
             for (RouterRecord routerRecord : routerRecords) {
-                try {
-                    routerRecord.mRouter.notifySessionInfoChanged(sessionInfo);
-                } catch (RemoteException ex) {
-                    Slog.w(TAG, "Failed to notify session info changed. Router probably died.", ex);
-                }
+                routerRecord.notifySessionInfoChanged(sessionInfo);
             }
         }
 
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 4d134b6..b440e88 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -137,6 +138,7 @@
     private final String mDefaultAudioRouteId;
     private final String mBluetoothA2dpRouteId;
 
+    @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
     public MediaRouterService(Context context) {
         mService2 = new MediaRouter2ServiceImpl(context);
         mContext = context;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6d2d2e4..426bc5e 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -55,7 +55,6 @@
             SystemMediaRoute2Provider.class.getPackage().getName(),
             SystemMediaRoute2Provider.class.getName());
 
-    static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
     static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";
 
     private final AudioManager mAudioManager;
@@ -170,7 +169,7 @@
             Bundle sessionHints) {
         // Assume a router without MODIFY_AUDIO_ROUTING permission can't request with
         // a route ID different from the default route ID. The service should've filtered.
-        if (TextUtils.equals(routeId, DEFAULT_ROUTE_ID)) {
+        if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
             mCallback.onSessionCreated(this, requestId, mDefaultSessionInfo);
             return;
         }
@@ -213,7 +212,7 @@
 
     @Override
     public void transferToRoute(long requestId, String sessionId, String routeId) {
-        if (TextUtils.equals(routeId, DEFAULT_ROUTE_ID)) {
+        if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
             // The currently selected route is the default route.
             return;
         }
@@ -326,10 +325,11 @@
                 builder.addTransferableRoute(deviceRoute.getId());
             }
             mSelectedRouteId = selectedRoute.getId();
-            mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute)
-                    .setSystemRoute(true)
-                    .setProviderId(mUniqueId)
-                    .build();
+            mDefaultRoute =
+                    new MediaRoute2Info.Builder(MediaRoute2Info.ROUTE_ID_DEFAULT, selectedRoute)
+                            .setSystemRoute(true)
+                            .setProviderId(mUniqueId)
+                            .build();
             builder.addSelectedRoute(mSelectedRouteId);
 
             for (MediaRoute2Info route : mBluetoothRouteController.getTransferableRoutes()) {
@@ -363,12 +363,13 @@
                 }
                 mSessionInfos.clear();
                 mSessionInfos.add(newSessionInfo);
-                mDefaultSessionInfo = new RoutingSessionInfo.Builder(
-                        SYSTEM_SESSION_ID, "" /* clientPackageName */)
-                        .setProviderId(mUniqueId)
-                        .setSystemSession(true)
-                        .addSelectedRoute(DEFAULT_ROUTE_ID)
-                        .build();
+                mDefaultSessionInfo =
+                        new RoutingSessionInfo.Builder(
+                                        SYSTEM_SESSION_ID, "" /* clientPackageName */)
+                                .setProviderId(mUniqueId)
+                                .setSystemSession(true)
+                                .addSelectedRoute(MediaRoute2Info.ROUTE_ID_DEFAULT)
+                                .build();
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index affe67d..5f28e56 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 package com.android.server.pm;
+
 import static android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES;
 import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
@@ -333,9 +334,10 @@
     }
 
     private boolean isCrossProfilePackageAllowlisted(String packageName) {
+        int userId = mInjector.getCallingUserId();
         return mInjector.withCleanCallingIdentity(() ->
                 mInjector.getDevicePolicyManagerInternal()
-                        .getAllCrossProfilePackages().contains(packageName));
+                        .getAllCrossProfilePackages(userId).contains(packageName));
     }
 
     private boolean isCrossProfilePackageAllowlistedByDefault(String packageName) {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 6032fec..8815834 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -238,6 +238,7 @@
                     UserManager.DISALLOW_CONFIG_DATE_TIME,
                     UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
                     UserManager.DISALLOW_CHANGE_WIFI_STATE,
+                    UserManager.DISALLOW_DEBUGGING_FEATURES,
                     UserManager.DISALLOW_WIFI_TETHERING,
                     UserManager.DISALLOW_WIFI_DIRECT,
                     UserManager.DISALLOW_ADD_WIFI_CONFIG,
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 4908529..7a43728 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -3031,9 +3031,6 @@
             }
         }
 
-        final boolean fromForegroundApp = Binder.withCleanCallingIdentity(() ->
-                mActivityManager.getPackageImportance(callingPackage) == IMPORTANCE_FOREGROUND);
-
         synchronized (mLock) {
             if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
             WallpaperData wallpaper;
@@ -3066,7 +3063,7 @@
                     wallpaper.mSystemWasBoth = systemIsBoth;
                     wallpaper.mWhich = which;
                     wallpaper.setComplete = completion;
-                    wallpaper.fromForegroundApp = fromForegroundApp;
+                    wallpaper.fromForegroundApp = isFromForegroundApp(callingPackage);
                     wallpaper.cropHint.set(cropHint);
                     wallpaper.allowBackup = allowBackup;
                     wallpaper.mWallpaperDimAmount = getWallpaperDimAmount();
@@ -3153,27 +3150,28 @@
             @SetWallpaperFlags int which, int userId) {
 
         if (isWallpaperSupported(callingPackage) && isSetWallpaperAllowed(callingPackage)) {
-            setWallpaperComponent(name, which, userId);
+            setWallpaperComponent(name, callingPackage, which, userId);
         }
     }
 
     // ToDo: Remove this version of the function
     @Override
     public void setWallpaperComponent(ComponentName name) {
-        setWallpaperComponent(name, UserHandle.getCallingUserId(), FLAG_SYSTEM);
+        setWallpaperComponent(name, "", UserHandle.getCallingUserId(), FLAG_SYSTEM);
     }
 
     @VisibleForTesting
-    void setWallpaperComponent(ComponentName name, @SetWallpaperFlags int which, int userId) {
+    void setWallpaperComponent(ComponentName name, String callingPackage,
+            @SetWallpaperFlags int which, int userId) {
         if (mIsLockscreenLiveWallpaperEnabled) {
-            setWallpaperComponentInternal(name, which, userId);
+            setWallpaperComponentInternal(name, callingPackage, which, userId);
         } else {
-            setWallpaperComponentInternalLegacy(name, which, userId);
+            setWallpaperComponentInternalLegacy(name, callingPackage, which, userId);
         }
     }
 
-    private void setWallpaperComponentInternal(ComponentName name, @SetWallpaperFlags int which,
-            int userIdIn) {
+    private void setWallpaperComponentInternal(ComponentName name, String callingPackage,
+            @SetWallpaperFlags int which, int userIdIn) {
         if (DEBUG) {
             Slog.v(TAG, "Setting new live wallpaper: which=" + which + ", component: " + name);
         }
@@ -3209,6 +3207,7 @@
                 newWallpaper.imageWallpaperPending = false;
                 newWallpaper.mWhich = which;
                 newWallpaper.mSystemWasBoth = systemIsBoth;
+                newWallpaper.fromForegroundApp = isFromForegroundApp(callingPackage);
                 final WallpaperDestinationChangeHandler
                         liveSync = new WallpaperDestinationChangeHandler(
                         newWallpaper);
@@ -3280,7 +3279,7 @@
     }
 
     // TODO(b/266818039) Remove this method
-    private void setWallpaperComponentInternalLegacy(ComponentName name,
+    private void setWallpaperComponentInternalLegacy(ComponentName name, String callingPackage,
             @SetWallpaperFlags int which, int userId) {
         userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
                 false /* all */, true /* full */, "changing live wallpaper", null /* pkg */);
@@ -3320,6 +3319,7 @@
             try {
                 wallpaper.imageWallpaperPending = false;
                 wallpaper.mWhich = which;
+                wallpaper.fromForegroundApp = isFromForegroundApp(callingPackage);
                 boolean same = changingToSame(name, wallpaper);
                 if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
                     if (!same) {
@@ -3679,6 +3679,11 @@
         }
     }
 
+    private boolean isFromForegroundApp(String callingPackage) {
+        return Binder.withCleanCallingIdentity(() ->
+                mActivityManager.getPackageImportance(callingPackage) == IMPORTANCE_FOREGROUND);
+    }
+
     /**
      * Certain user types do not support wallpapers (e.g. managed profiles). The check is
      * implemented through through the OP_WRITE_WALLPAPER AppOp.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index fd74dac..0b7618d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -93,6 +93,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_METADATA;
 import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_OVERRIDE;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_METADATA;
 import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
 import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED;
 import static android.content.res.Configuration.EMPTY;
@@ -1298,7 +1299,7 @@
                         + info.getManifestMinAspectRatio());
             }
             pw.println(prefix + "supportsSizeChanges="
-                    + ActivityInfo.sizeChangesSupportModeToString(info.supportsSizeChanges()));
+                    + ActivityInfo.sizeChangesSupportModeToString(supportsSizeChanges()));
             if (info.configChanges != 0) {
                 pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
             }
@@ -8127,7 +8128,7 @@
      *         aspect ratio.
      */
     boolean shouldCreateCompatDisplayInsets() {
-        switch (info.supportsSizeChanges()) {
+        switch (supportsSizeChanges()) {
             case SIZE_CHANGES_SUPPORTED_METADATA:
             case SIZE_CHANGES_SUPPORTED_OVERRIDE:
                 return false;
@@ -8154,6 +8155,26 @@
                 && isActivityTypeStandardOrUndefined();
     }
 
+    /**
+     * Returns whether the activity supports size changes.
+     */
+    @ActivityInfo.SizeChangesSupportMode
+    private int supportsSizeChanges() {
+        if (mLetterboxUiController.shouldOverrideForceNonResizeApp()) {
+            return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
+        }
+
+        if (info.supportsSizeChanges) {
+            return SIZE_CHANGES_SUPPORTED_METADATA;
+        }
+
+        if (mLetterboxUiController.shouldOverrideForceResizeApp()) {
+            return SIZE_CHANGES_SUPPORTED_OVERRIDE;
+        }
+
+        return SIZE_CHANGES_UNSUPPORTED_METADATA;
+    }
+
     @Override
     boolean hasSizeCompatBounds() {
         return mSizeCompatBounds != null;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0b960ec..b67ccd2 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -439,7 +439,9 @@
                                 ? mDisplayContent.getImeInputTarget().getActivityRecord() : null;
                         if (app != null) {
                             mDisplayContent.removeImeSurfaceImmediately();
-                            mDisplayContent.mAtmService.takeTaskSnapshot(app.getTask().mTaskId);
+                            if (app.getTask() != null) {
+                                mDisplayContent.mAtmService.takeTaskSnapshot(app.getTask().mTaskId);
+                            }
                         }
                     } else {
                         // Disable IME icon explicitly when IME attached to the app in case
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index a158e8d2..d83c861 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -17,6 +17,8 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
+import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
 import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
@@ -52,6 +54,7 @@
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
 import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
 import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
 
@@ -189,6 +192,10 @@
 
     // Corresponds to OVERRIDE_MIN_ASPECT_RATIO
     private final boolean mIsOverrideMinAspectRatio;
+    // Corresponds to FORCE_RESIZE_APP
+    private final boolean mIsOverrideForceResizeApp;
+    // Corresponds to FORCE_NON_RESIZE_APP
+    private final boolean mIsOverrideForceNonResizeApp;
 
     @Nullable
     private final Boolean mBooleanPropertyAllowOrientationOverride;
@@ -196,6 +203,8 @@
     private final Boolean mBooleanPropertyAllowDisplayOrientationOverride;
     @Nullable
     private final Boolean mBooleanPropertyAllowMinAspectRatioOverride;
+    @Nullable
+    private final Boolean mBooleanPropertyAllowForceResizeOverride;
 
     /*
      * WindowContainerListener responsible to make translucent activities inherit
@@ -311,6 +320,10 @@
                 readComponentProperty(packageManager, mActivityRecord.packageName,
                         /* gatingCondition */ null,
                         PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+        mBooleanPropertyAllowForceResizeOverride =
+                readComponentProperty(packageManager, mActivityRecord.packageName,
+                        /* gatingCondition */ null,
+                        PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
 
         mIsOverrideAnyOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION);
         mIsOverrideToPortraitOrientationEnabled =
@@ -342,6 +355,8 @@
         mIsOverrideEnableCompatFakeFocusEnabled =
                 isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS);
         mIsOverrideMinAspectRatio = isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO);
+        mIsOverrideForceResizeApp = isCompatChangeEnabled(FORCE_RESIZE_APP);
+        mIsOverrideForceNonResizeApp = isCompatChangeEnabled(FORCE_NON_RESIZE_APP);
     }
 
     /**
@@ -533,6 +548,42 @@
     }
 
     /**
+     * Whether we should apply the force resize per-app override. When this override is applied it
+     * forces the packages it is applied to to be resizable. It won't change whether the app can be
+     * put into multi-windowing mode, but allow the app to resize without going into size-compat
+     * mode when the window container resizes, such as display size change or screen rotation.
+     *
+     * <p>This method returns {@code true} when the following conditions are met:
+     * <ul>
+     *     <li>Opt-out component property isn't enabled
+     *     <li>Per-app override is enabled
+     * </ul>
+     */
+    boolean shouldOverrideForceResizeApp() {
+        return shouldEnableWithOptInOverrideAndOptOutProperty(
+                /* gatingCondition */ () -> true,
+                mIsOverrideForceResizeApp,
+                mBooleanPropertyAllowForceResizeOverride);
+    }
+
+    /**
+     * Whether we should apply the force non resize per-app override. When this override is applied
+     * it forces the packages it is applied to to be non-resizable.
+     *
+     * <p>This method returns {@code true} when the following conditions are met:
+     * <ul>
+     *     <li>Opt-out component property isn't enabled
+     *     <li>Per-app override is enabled
+     * </ul>
+     */
+    boolean shouldOverrideForceNonResizeApp() {
+        return shouldEnableWithOptInOverrideAndOptOutProperty(
+                /* gatingCondition */ () -> true,
+                mIsOverrideForceNonResizeApp,
+                mBooleanPropertyAllowForceResizeOverride);
+    }
+
+    /**
      * Sets whether an activity is relaunching after the app has called {@link
      * android.app.Activity#setRequestedOrientation}.
      */
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index ee80a05..df968f9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -32,13 +32,17 @@
 import android.app.admin.DevicePolicyIdentifiers;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyState;
+import android.app.admin.IntentFilterPolicyKey;
 import android.app.admin.PolicyKey;
 import android.app.admin.PolicyUpdateReceiver;
 import android.app.admin.PolicyValue;
 import android.app.admin.TargetUser;
 import android.app.admin.UserRestrictionPolicyKey;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
@@ -1043,10 +1047,56 @@
             }
             if (updatedPackage != null) {
                 updateDeviceAdminServiceOnPackageChanged(updatedPackage, userId);
+                removePersistentPreferredActivityPoliciesForPackage(updatedPackage, userId);
             }
         });
     }
 
+    private void removePersistentPreferredActivityPoliciesForPackage(
+            @NonNull String packageName, int userId) {
+        Set<PolicyKey> policyKeys = getLocalPolicyKeysSetByAllAdmins(
+                PolicyDefinition.GENERIC_PERSISTENT_PREFERRED_ACTIVITY, userId);
+        for (PolicyKey key : policyKeys) {
+            if (!(key instanceof IntentFilterPolicyKey)) {
+                throw new IllegalStateException("PolicyKey for "
+                        + "PERSISTENT_PREFERRED_ACTIVITY is not of type "
+                        + "IntentFilterPolicyKey");
+            }
+            IntentFilterPolicyKey parsedKey =
+                    (IntentFilterPolicyKey) key;
+            IntentFilter intentFilter = Objects.requireNonNull(parsedKey.getIntentFilter());
+            PolicyDefinition<ComponentName> policyDefinition =
+                    PolicyDefinition.PERSISTENT_PREFERRED_ACTIVITY(intentFilter);
+            LinkedHashMap<EnforcingAdmin, PolicyValue<ComponentName>> policies =
+                    getLocalPoliciesSetByAdmins(
+                            policyDefinition,
+                            userId);
+            IPackageManager packageManager = AppGlobals.getPackageManager();
+            for (EnforcingAdmin admin : policies.keySet()) {
+                if (policies.get(admin).getValue() != null
+                        && policies.get(admin).getValue().getPackageName().equals(packageName)) {
+                    try {
+                        if (packageManager.getPackageInfo(
+                                packageName, 0, userId) == null
+                                || packageManager.getReceiverInfo(policies.get(admin).getValue(),
+                                PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                                userId) == null) {
+                            Slogf.e(TAG, String.format(
+                                    "Persistent preferred activity in package %s not found for "
+                                            + "user %d, removing policy for admin",
+                                    packageName, userId));
+                            removeLocalPolicy(policyDefinition, admin, userId);
+                        }
+                    } catch (RemoteException re) {
+                        // Shouldn't happen.
+                        Slogf.wtf(TAG, "Error handling package changes", re);
+                    }
+                }
+            }
+        }
+    }
+
     private boolean isPackageInstalled(String packageName, int userId) {
         try {
             return AppGlobals.getPackageManager().getPackageInfo(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8f7e292..f875e34 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11447,6 +11447,10 @@
                         || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
                 enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName);
             }
+            if (!isPackageInstalledForUser(activity.getPackageName(), userId)) {
+                // Fail early as packageManager doesn't persist the activity if its not installed.
+                return;
+            }
             mDevicePolicyEngine.setLocalPolicy(
                     PolicyDefinition.PERSISTENT_PREFERRED_ACTIVITY(filter),
                     enforcingAdmin,
@@ -13389,14 +13393,8 @@
                 PolicyDefinition<Boolean> policyDefinition =
                         PolicyDefinition.getPolicyDefinitionForUserRestriction(key);
                 if (enabledFromThisOwner) {
-                    // TODO: Remove this special case - replace with breaking change to require
-                    //  setGlobally to disable ADB
-                    if (key.equals(UserManager.DISALLOW_DEBUGGING_FEATURES) && parent) {
-                        setGlobalUserRestrictionInternal(admin, key, /* enabled= */ true);
-                    } else {
-                        setLocalUserRestrictionInternal(
-                                admin, key, /* enabled= */ true, affectedUserId);
-                    }
+                    setLocalUserRestrictionInternal(
+                            admin, key, /* enabled= */ true, affectedUserId);
                 } else {
                     // Remove any local and global policy that was set by the admin
                     if (!policyDefinition.isLocalOnlyPolicy()) {
@@ -13914,7 +13912,6 @@
         CallerIdentity caller = getCallerIdentity(who, callerPackage);
         final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
         if (isPolicyEngineForFinanceFlagEnabled()) {
-            // TODO: We need to ensure the delegate with DELEGATION_PACKAGE_ACCESS can do this
             enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId);
         } else {
             Preconditions.checkCallAuthorization((caller.hasAdminComponent()
@@ -16085,8 +16082,8 @@
         }
 
         @Override
-        public List<String> getAllCrossProfilePackages() {
-            return DevicePolicyManagerService.this.getAllCrossProfilePackages();
+        public List<String> getAllCrossProfilePackages(int userId) {
+            return DevicePolicyManagerService.this.getAllCrossProfilePackages(userId);
         }
 
         @Override
@@ -20305,7 +20302,7 @@
     }
 
     @Override
-    public List<String> getAllCrossProfilePackages() {
+    public List<String> getAllCrossProfilePackages(int userId) {
         if (!mHasFeature) {
             return Collections.emptyList();
         }
@@ -20314,10 +20311,10 @@
                 isSystemUid(caller) || isRootUid(caller) || hasCallingPermission(
                         permission.INTERACT_ACROSS_USERS) || hasCallingPermission(
                         permission.INTERACT_ACROSS_USERS_FULL) || hasPermissionForPreflight(
-                        caller, permission.INTERACT_ACROSS_PROFILES));
+                                                caller, permission.INTERACT_ACROSS_PROFILES));
 
         synchronized (getLockObject()) {
-            final List<ActiveAdmin> admins = getProfileOwnerAdminsForCurrentProfileGroup();
+            final List<ActiveAdmin> admins = getProfileOwnerAdminsForProfileGroup(userId);
             final List<String> packages = getCrossProfilePackagesForAdmins(admins);
 
             packages.addAll(getDefaultCrossProfilePackages());
@@ -20346,11 +20343,10 @@
         return new ArrayList<>(crossProfilePackages);
     }
 
-    private List<ActiveAdmin> getProfileOwnerAdminsForCurrentProfileGroup() {
+    private List<ActiveAdmin> getProfileOwnerAdminsForProfileGroup(int userId) {
         synchronized (getLockObject()) {
             final List<ActiveAdmin> admins = new ArrayList<>();
-            int[] users = mUserManager.getProfileIdsWithDisabled(
-                    mInjector.userHandleGetCallingUserId());
+            int[] users = mUserManager.getProfileIdsWithDisabled(userId);
             for (int i = 0; i < users.length; i++) {
                 final ComponentName componentName = getProfileOwnerAsUser(users[i]);
                 if (componentName != null) {
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index d128e68..4e46836 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -201,7 +201,7 @@
     }
 
     private void mockCrossProfileAppWhitelisted() {
-        when(mDevicePolicyManagerInternal.getAllCrossProfilePackages())
+        when(mDevicePolicyManagerInternal.getAllCrossProfilePackages(anyInt()))
                 .thenReturn(Lists.newArrayList(CROSS_PROFILE_APP_PACKAGE_NAME));
     }
 
@@ -662,7 +662,7 @@
     }
 
     private void mockCrossProfileAppNotWhitelisted() {
-        when(mDevicePolicyManagerInternal.getAllCrossProfilePackages())
+        when(mDevicePolicyManagerInternal.getAllCrossProfilePackages(anyInt()))
                 .thenReturn(new ArrayList<>());
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 3d0163d..51e521d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -292,7 +292,8 @@
         verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent);
         verifyCurrentSystemData(testUserId);
 
-        mService.setWallpaperComponent(sImageWallpaperComponentName, FLAG_SYSTEM, testUserId);
+        mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
+                FLAG_SYSTEM, testUserId);
         verifyLastWallpaperData(testUserId, sImageWallpaperComponentName);
         verifyCurrentSystemData(testUserId);
 
@@ -321,7 +322,8 @@
 
         WallpaperManagerService.DisplayConnector connector =
                 mService.mLastWallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY);
-        mService.setWallpaperComponent(sDefaultWallpaperComponent, FLAG_SYSTEM, testUserId);
+        mService.setWallpaperComponent(sDefaultWallpaperComponent, sContext.getOpPackageName(),
+                FLAG_SYSTEM, testUserId);
 
         verify(connector.mEngine).dispatchWallpaperCommand(
                 eq(COMMAND_REAPPLY), anyInt(), anyInt(), anyInt(), any());
@@ -465,7 +467,8 @@
     public void testGetAdjustedWallpaperColorsOnDimming() throws RemoteException {
         final int testUserId = USER_SYSTEM;
         mService.switchUser(testUserId, null);
-        mService.setWallpaperComponent(sDefaultWallpaperComponent, FLAG_SYSTEM, testUserId);
+        mService.setWallpaperComponent(sDefaultWallpaperComponent, sContext.getOpPackageName(),
+                FLAG_SYSTEM, testUserId);
         WallpaperData wallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM, testUserId);
 
         // Mock a wallpaper data with color hints that support dark text and dark theme
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java
index ccddb2fd..1475537 100644
--- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java
@@ -75,7 +75,7 @@
         callMetadataSyncData.addCall(call);
         mSyncConnectionService.mCrossDeviceSyncControllerCallback.processContextSyncMessage(
                 /* associationId= */ 0, callMetadataSyncData);
-        verify(mMockTelecomManager, times(1)).addNewIncomingCall(any(), any());
+        verify(mMockTelecomManager, times(0)).addNewIncomingCall(any(), any());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java
index 5a0646c..6a939ab 100644
--- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java
@@ -23,6 +23,7 @@
 import android.telecom.Call;
 import android.telecom.ParcelableCall;
 import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.InstrumentationRegistry;
@@ -45,7 +46,7 @@
     @Test
     public void updateCallDetails_uninitialized() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
                 .isEqualTo(android.companion.Telecom.Call.UNKNOWN_STATUS);
@@ -55,7 +56,7 @@
     @Test
     public void updateCallDetails_ringing() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
                 Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
@@ -70,7 +71,7 @@
     @Test
     public void updateCallDetails_ongoing() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
                 Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
@@ -85,7 +86,7 @@
     @Test
     public void updateCallDetails_holding() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_HOLDING,
                 Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
@@ -99,7 +100,7 @@
     @Test
     public void updateCallDetails_cannotHold() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.updateCallDetails(
                 createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_MUTE));
@@ -113,7 +114,7 @@
     @Test
     public void updateCallDetails_cannotMute() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.updateCallDetails(
                 createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_HOLD));
@@ -127,7 +128,7 @@
     @Test
     public void updateCallDetails_transitionRingingToOngoing() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
                 Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
@@ -150,7 +151,7 @@
     @Test
     public void updateSilencedIfRinging_ringing_silenced() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
                 Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
@@ -165,7 +166,7 @@
     @Test
     public void updateSilencedIfRinging_notRinging_notSilenced() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
                 Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
@@ -179,12 +180,11 @@
     }
 
     @Test
-    public void getReadableCallerId_enterpriseCall_adminBlocked_ott() {
+    public void getReadableCallerId_enterpriseCall_adminBlocked_hasContact() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.mIsEnterprise = true;
-        crossDeviceCall.mIsOtt = true;
         crossDeviceCall.updateCallDetails(
                 createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
 
@@ -195,76 +195,74 @@
     }
 
     @Test
-    public void getReadableCallerId_enterpriseCall_adminUnblocked_ott() {
+    public void getReadableCallerId_enterpriseCall_adminUnblocked_hasContact() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.mIsEnterprise = true;
-        crossDeviceCall.mIsOtt = true;
         crossDeviceCall.updateCallDetails(
                 createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
 
         final String result = crossDeviceCall.getReadableCallerId(false);
 
         assertWithMessage("Wrong caller id").that(result)
+                .isEqualTo(CONTACT_DISPLAY_NAME);
+    }
+
+    @Test
+    public void getReadableCallerId_enterpriseCall_adminBlocked_noContact() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext(),
+                mUninitializedCallDetails, /* callAudioState= */ null);
+        crossDeviceCall.mIsEnterprise = true;
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0, /* hasContactName= */
+                        false));
+
+        final String result = crossDeviceCall.getReadableCallerId(true);
+
+        assertWithMessage("Wrong caller id").that(result)
+                .isEqualTo(CALLER_DISPLAY_NAME);
+    }
+
+    @Test
+    public void getReadableCallerId_nonEnterpriseCall_adminBlocked_noContact() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext(),
+                mUninitializedCallDetails, /* callAudioState= */ null);
+        crossDeviceCall.mIsEnterprise = false;
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0, /* hasContactName= */
+                        false));
+
+        final String result = crossDeviceCall.getReadableCallerId(true);
+
+        assertWithMessage("Wrong caller id").that(result)
+                .isEqualTo(CALLER_DISPLAY_NAME);
+    }
+
+    @Test
+    public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_noContact() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext(),
+                mUninitializedCallDetails, /* callAudioState= */ null);
+        crossDeviceCall.mIsEnterprise = false;
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0, /* hasContactName= */
+                        false));
+
+        final String result = crossDeviceCall.getReadableCallerId(false);
+
+        assertWithMessage("Wrong caller id").that(result)
                 .isEqualTo(CALLER_DISPLAY_NAME);
     }
 
     @Test
-    public void getReadableCallerId_enterpriseCall_adminBlocked_pstn() {
+    public void getReadableCallerId_nonEnterpriseCall_adminBlocked_hasContact() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
-                mUninitializedCallDetails, /* callAudioState= */ null);
-        crossDeviceCall.mIsEnterprise = true;
-        crossDeviceCall.mIsOtt = false;
-        crossDeviceCall.updateCallDetails(
-                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
-
-        final String result = crossDeviceCall.getReadableCallerId(true);
-
-        assertWithMessage("Wrong caller id").that(result)
-                .isEqualTo(CALLER_DISPLAY_NAME);
-    }
-
-    @Test
-    public void getReadableCallerId_nonEnterpriseCall_adminBlocked_ott() {
-        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.mIsEnterprise = false;
-        crossDeviceCall.mIsOtt = true;
-        crossDeviceCall.updateCallDetails(
-                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
-
-        final String result = crossDeviceCall.getReadableCallerId(true);
-
-        assertWithMessage("Wrong caller id").that(result)
-                .isEqualTo(CALLER_DISPLAY_NAME);
-    }
-
-    @Test
-    public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_ott() {
-        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
-                mUninitializedCallDetails, /* callAudioState= */ null);
-        crossDeviceCall.mIsEnterprise = false;
-        crossDeviceCall.mIsOtt = true;
-        crossDeviceCall.updateCallDetails(
-                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
-
-        final String result = crossDeviceCall.getReadableCallerId(false);
-
-        assertWithMessage("Wrong caller id").that(result)
-                .isEqualTo(CALLER_DISPLAY_NAME);
-    }
-
-    @Test
-    public void getReadableCallerId_nonEnterpriseCall_adminBlocked_pstn() {
-        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
-                mUninitializedCallDetails, /* callAudioState= */ null);
-        crossDeviceCall.mIsEnterprise = false;
-        crossDeviceCall.mIsOtt = false;
         crossDeviceCall.updateCallDetails(
                 createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
 
@@ -275,12 +273,11 @@
     }
 
     @Test
-    public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_pstn() {
+    public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_hasContact() {
         final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
-                InstrumentationRegistry.getTargetContext().getPackageManager(),
+                InstrumentationRegistry.getTargetContext(),
                 mUninitializedCallDetails, /* callAudioState= */ null);
         crossDeviceCall.mIsEnterprise = false;
-        crossDeviceCall.mIsOtt = false;
         crossDeviceCall.updateCallDetails(
                 createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
 
@@ -291,10 +288,17 @@
     }
 
     private Call.Details createCallDetails(int state, int capabilities) {
+        return createCallDetails(state, capabilities, /* hasContactName= */ true);
+    }
+
+    private Call.Details createCallDetails(int state, int capabilities, boolean hasContactName) {
         final ParcelableCall.ParcelableCallBuilder parcelableCallBuilder =
                 new ParcelableCall.ParcelableCallBuilder();
         parcelableCallBuilder.setCallerDisplayName(CALLER_DISPLAY_NAME);
-        parcelableCallBuilder.setContactDisplayName(CONTACT_DISPLAY_NAME);
+        if (hasContactName) {
+            parcelableCallBuilder.setContactDisplayName(CONTACT_DISPLAY_NAME);
+        }
+        parcelableCallBuilder.setCallerDisplayNamePresentation(TelecomManager.PRESENTATION_ALLOWED);
         parcelableCallBuilder.setCapabilities(capabilities);
         parcelableCallBuilder.setState(state);
         parcelableCallBuilder.setConferenceableCallIds(Collections.emptyList());
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java
index 33e7cd2..7688fcb 100644
--- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -211,4 +212,62 @@
         verify(mMockTelecomManager, times(1)).registerPhoneAccount(any());
         verify(mMockTelecomManager, times(1)).unregisterPhoneAccount(any());
     }
+
+    @Test
+    public void updateCalls_newCall() {
+        final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
+        call.setId("123abc");
+        call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test"));
+        final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData();
+        callMetadataSyncData.addCall(call);
+        final CrossDeviceSyncController.CallManager callManager =
+                new CrossDeviceSyncController.CallManager(mMockContext,
+                        new CrossDeviceSyncController.PhoneAccountManager(mMockContext));
+        callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData);
+        verify(mMockTelecomManager, times(1)).addNewIncomingCall(any(), any());
+    }
+
+    @Test
+    public void updateCalls_newCall_noFacilitator() {
+        final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
+        call.setId("123abc");
+        final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData();
+        callMetadataSyncData.addCall(call);
+        final CrossDeviceSyncController.CallManager callManager =
+                new CrossDeviceSyncController.CallManager(mMockContext,
+                        new CrossDeviceSyncController.PhoneAccountManager(mMockContext));
+        callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData);
+        verify(mMockTelecomManager, times(0)).addNewIncomingCall(any(), any());
+    }
+
+    @Test
+    public void updateCalls_existingCall() {
+        final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
+        call.setId("123abc");
+        final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData();
+        callMetadataSyncData.addCall(call);
+        final CrossDeviceSyncController.CallManager callManager =
+                new CrossDeviceSyncController.CallManager(mMockContext,
+                        new CrossDeviceSyncController.PhoneAccountManager(mMockContext));
+        callManager.mCallIds.put(/* associationId= */ 0, Set.of(call.getId()));
+        callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData);
+        verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any());
+    }
+
+    @Test
+    public void updateCalls_removedCall() {
+        final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
+        call.setId("123abc");
+        final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData();
+        callMetadataSyncData.addCall(call);
+        final CrossDeviceSyncController.CallManager callManager =
+                new CrossDeviceSyncController.CallManager(mMockContext,
+                        new CrossDeviceSyncController.PhoneAccountManager(mMockContext));
+        callManager.mCallIds.put(/* associationId= */ 0, Set.of(call.getId(), "fakeCallId"));
+        callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData);
+        verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any());
+        assertWithMessage("Hasn't removed the id of the removed call")
+                .that(callManager.mCallIds)
+                .containsExactly(/* associationId= */ 0, Set.of(call.getId()));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 4881012..6684150 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -75,6 +75,10 @@
     private static final DeviceState UNSUPPORTED_DEVICE_STATE =
             new DeviceState(255, "UNSUPPORTED", 0 /* flags */);
 
+    private static final int[] SUPPORTED_DEVICE_STATE_IDENTIFIERS =
+            new int[]{DEFAULT_DEVICE_STATE.getIdentifier(), OTHER_DEVICE_STATE.getIdentifier(),
+                    DEVICE_STATE_CANCEL_WHEN_REQUESTER_NOT_ON_TOP.getIdentifier()};
+
     private static final int FAKE_PROCESS_ID = 100;
 
     private TestDeviceStatePolicy mPolicy;
@@ -267,14 +271,26 @@
     public void getDeviceStateInfo() throws RemoteException {
         DeviceStateInfo info = mService.getBinderService().getDeviceStateInfo();
         assertNotNull(info);
-        assertArrayEquals(info.supportedStates,
-                new int[] { DEFAULT_DEVICE_STATE.getIdentifier(),
-                        OTHER_DEVICE_STATE.getIdentifier(),
-                        DEVICE_STATE_CANCEL_WHEN_REQUESTER_NOT_ON_TOP.getIdentifier()});
+        assertArrayEquals(info.supportedStates, SUPPORTED_DEVICE_STATE_IDENTIFIERS);
         assertEquals(info.baseState, DEFAULT_DEVICE_STATE.getIdentifier());
         assertEquals(info.currentState, DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
+    @Test
+    public void getDeviceStateInfo_baseStateAndCommittedStateNotSet() throws RemoteException {
+        // Create a provider and a service without an initial base state.
+        mProvider = new TestDeviceStateProvider(null /* initialState */);
+        mPolicy = new TestDeviceStatePolicy(mProvider);
+        setupDeviceStateManagerService();
+        flushHandler(); // Flush the handler to ensure the initial values are committed.
+
+        DeviceStateInfo info = mService.getBinderService().getDeviceStateInfo();
+
+        assertArrayEquals(info.supportedStates, SUPPORTED_DEVICE_STATE_IDENTIFIERS);
+        assertEquals(info.baseState, INVALID_DEVICE_STATE);
+        assertEquals(info.currentState, INVALID_DEVICE_STATE);
+    }
+
     @FlakyTest(bugId = 223153452)
     @Test
     public void registerCallback() throws RemoteException {
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 67e7470..7d507e9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
+import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
 import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
@@ -43,6 +45,7 @@
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
 import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
 import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
 
@@ -942,6 +945,118 @@
     }
 
     @Test
+    @EnableCompatChanges({FORCE_RESIZE_APP})
+    public void testshouldOverrideForceResizeApp_overrideEnabled_returnsTrue() {
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertTrue(mController.shouldOverrideForceResizeApp());
+    }
+
+    @Test
+    @EnableCompatChanges({FORCE_RESIZE_APP})
+    public void testshouldOverrideForceResizeApp_propertyTrue_overrideEnabled_returnsTrue()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ true);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertTrue(mController.shouldOverrideForceResizeApp());
+    }
+
+    @Test
+    @DisableCompatChanges({FORCE_RESIZE_APP})
+    public void testshouldOverrideForceResizeApp_propertyTrue_overrideDisabled_returnsFalse()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ true);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideForceResizeApp());
+    }
+
+    @Test
+    @DisableCompatChanges({FORCE_RESIZE_APP})
+    public void testshouldOverrideForceResizeApp_overrideDisabled_returnsFalse() {
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideForceResizeApp());
+    }
+
+    @Test
+    @EnableCompatChanges({FORCE_RESIZE_APP})
+    public void testshouldOverrideForceResizeApp_propertyFalse_overrideEnabled_returnsFalse()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ false);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideForceResizeApp());
+    }
+
+    @Test
+    @DisableCompatChanges({FORCE_RESIZE_APP})
+    public void testshouldOverrideForceResizeApp_propertyFalse_noOverride_returnsFalse()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ false);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideForceResizeApp());
+    }
+
+    @Test
+    @EnableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testshouldOverrideForceNonResizeApp_overrideEnabled_returnsTrue() {
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertTrue(mController.shouldOverrideForceNonResizeApp());
+    }
+
+    @Test
+    @EnableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testshouldOverrideForceNonResizeApp_propertyTrue_overrideEnabled_returnsTrue()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ true);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertTrue(mController.shouldOverrideForceNonResizeApp());
+    }
+
+    @Test
+    @DisableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testshouldOverrideForceNonResizeApp_propertyTrue_overrideDisabled_returnsFalse()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ true);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideForceNonResizeApp());
+    }
+
+    @Test
+    @DisableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testshouldOverrideForceNonResizeApp_overrideDisabled_returnsFalse() {
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideForceNonResizeApp());
+    }
+
+    @Test
+    @EnableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testshouldOverrideForceNonResizeApp_propertyFalse_overrideEnabled_returnsFalse()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ false);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideForceNonResizeApp());
+    }
+
+    @Test
+    @DisableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testshouldOverrideForceNonResizeApp_propertyFalse_noOverride_returnsFalse()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ false);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideForceNonResizeApp());
+    }
+
+    @Test
     public void testgetFixedOrientationLetterboxAspectRatio_splitScreenAspectEnabled() {
         doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration)
                 .isCameraCompatTreatmentEnabled(anyBoolean());
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 9fb5509..13945a1 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -366,10 +366,10 @@
             try {
                 int uid = mPackageManager.getPackageUid(mOriginatorIdentity.packageName,
                         PackageManager.PackageInfoFlags.of(0));
-                if (uid != mOriginatorIdentity.uid) {
-                    throw new SecurityException("Package name: " +
-                            mOriginatorIdentity.packageName + "with uid: " + uid
-                            + "attempted to spoof as: " + mOriginatorIdentity.uid);
+                if (!UserHandle.isSameApp(uid, mOriginatorIdentity.uid)) {
+                    throw new SecurityException("Uid " + mOriginatorIdentity.uid +
+                            " attempted to spoof package name " +
+                            mOriginatorIdentity.packageName + " with uid: " + uid);
                 }
             } catch (PackageManager.NameNotFoundException e) {
                 throw new SecurityException("Package name not found: "
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index edaaf3f..248cc26 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -962,7 +962,7 @@
         final DetectorSession session = mDetectorSessions.get(
                 HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR);
         if (session == null || session.isDestroyed()) {
-            Slog.v(TAG, "Not found the look and talk perceiver");
+            Slog.v(TAG, "Not found the visual query detector");
             return null;
         }
         return (VisualQueryDetectorSession) session;