Merge "Add permission access to DPM certificate mng APIs"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index c5510b7..6a7904c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -3744,7 +3744,7 @@
         // Enforce that only the app itself (or shared uid participant) can schedule a
         // job that runs one of the app's services, as well as verifying that the
         // named service properly requires the BIND_JOB_SERVICE permission
-        private void enforceValidJobRequest(int uid, JobInfo job) {
+        private void enforceValidJobRequest(int uid, int pid, JobInfo job) {
             final PackageManager pm = getContext()
                     .createContextAsUser(UserHandle.getUserHandleForUid(uid), 0)
                     .getPackageManager();
@@ -3768,6 +3768,10 @@
                 throw new IllegalArgumentException(
                         "Tried to schedule job for non-existent component: " + service);
             }
+            if (job.isPersisted() && !canPersistJobs(pid, uid)) {
+                throw new IllegalArgumentException("Requested job cannot be persisted without"
+                        + " holding android.permission.RECEIVE_BOOT_COMPLETED permission");
+            }
         }
 
         private boolean canPersistJobs(int pid, int uid) {
@@ -3915,13 +3919,7 @@
             final int uid = Binder.getCallingUid();
             final int userId = UserHandle.getUserId(uid);
 
-            enforceValidJobRequest(uid, job);
-            if (job.isPersisted()) {
-                if (!canPersistJobs(pid, uid)) {
-                    throw new IllegalArgumentException("Error: requested job be persisted without"
-                            + " holding RECEIVE_BOOT_COMPLETED permission.");
-                }
-            }
+            enforceValidJobRequest(uid, pid, job);
 
             final int result = validateJob(job, uid, -1, null, null);
             if (result != JobScheduler.RESULT_SUCCESS) {
@@ -3948,9 +3946,10 @@
                 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
             }
             final int uid = Binder.getCallingUid();
+            final int pid = Binder.getCallingPid();
             final int userId = UserHandle.getUserId(uid);
 
-            enforceValidJobRequest(uid, job);
+            enforceValidJobRequest(uid, pid, job);
             if (work == null) {
                 throw new NullPointerException("work is null");
             }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index d048cca..ceb47ea 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -107,6 +107,8 @@
     private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
     /** Amount of time the JobScheduler will wait for a response from an app for a message. */
     private static final long OP_TIMEOUT_MILLIS = 8 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
+    /** Amount of time the JobScheduler will wait for a job to provide a required notification. */
+    private static final long NOTIFICATION_TIMEOUT_MILLIS = 10_000L * Build.HW_TIMEOUT_MULTIPLIER;
 
     private static final String[] VERB_STRINGS = {
             "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED"
@@ -190,6 +192,8 @@
     private long mMinExecutionGuaranteeMillis;
     /** The absolute maximum amount of time the job can run */
     private long mMaxExecutionTimeMillis;
+    /** Whether this job is required to provide a notification and we're still waiting for it. */
+    private boolean mAwaitingNotification;
 
     private long mEstimatedDownloadBytes;
     private long mEstimatedUploadBytes;
@@ -348,6 +352,7 @@
             mEstimatedDownloadBytes = job.getEstimatedNetworkDownloadBytes();
             mEstimatedUploadBytes = job.getEstimatedNetworkUploadBytes();
             mTransferredDownloadBytes = mTransferredUploadBytes = 0;
+            mAwaitingNotification = job.isUserVisibleJob();
 
             final long whenDeferred = job.getWhenStandbyDeferred();
             if (whenDeferred > 0) {
@@ -771,6 +776,12 @@
                 mNotificationCoordinator.enqueueNotification(this, callingPkgName,
                         callingPid, callingUid, notificationId,
                         notification, jobEndNotificationPolicy);
+                if (mAwaitingNotification) {
+                    mAwaitingNotification = false;
+                    if (mVerb == VERB_EXECUTING) {
+                        scheduleOpTimeOutLocked();
+                    }
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -1183,6 +1194,8 @@
                 }
                 final long latestStopTimeElapsed =
                         mExecutionStartTimeElapsed + mMaxExecutionTimeMillis;
+                final long earliestStopTimeElapsed =
+                        mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis;
                 final long nowElapsed = sElapsedRealtimeClock.millis();
                 if (nowElapsed >= latestStopTimeElapsed) {
                     // Not an error - client ran out of time.
@@ -1191,7 +1204,7 @@
                     mParams.setStopReason(JobParameters.STOP_REASON_TIMEOUT,
                             JobParameters.INTERNAL_STOP_REASON_TIMEOUT, "client timed out");
                     sendStopMessageLocked("timeout while executing");
-                } else {
+                } else if (nowElapsed >= earliestStopTimeElapsed) {
                     // We've given the app the minimum execution time. See if we should stop it or
                     // let it continue running
                     final String reason = mJobConcurrencyManager.shouldStopRunningJobLocked(this);
@@ -1209,6 +1222,14 @@
                                 + " continue to run past min execution time");
                         scheduleOpTimeOutLocked();
                     }
+                } else if (mAwaitingNotification) {
+                    onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ true,
+                            /* debugReason */ "timed out while stopping",
+                            /* anrMessage */ "required notification not provided",
+                            /* triggerAnr */ true);
+                } else {
+                    Slog.e(TAG, "Unexpected op timeout while EXECUTING");
+                    scheduleOpTimeOutLocked();
                 }
                 break;
             default:
@@ -1402,20 +1423,24 @@
     private void scheduleOpTimeOutLocked() {
         removeOpTimeOutLocked();
 
-        // TODO(260848384): enforce setNotification timeout for user-initiated jobs
         final long timeoutMillis;
         switch (mVerb) {
             case VERB_EXECUTING:
+                long minTimeout;
                 final long earliestStopTimeElapsed =
                         mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis;
                 final long latestStopTimeElapsed =
                         mExecutionStartTimeElapsed + mMaxExecutionTimeMillis;
                 final long nowElapsed = sElapsedRealtimeClock.millis();
                 if (nowElapsed < earliestStopTimeElapsed) {
-                    timeoutMillis = earliestStopTimeElapsed - nowElapsed;
+                    minTimeout = earliestStopTimeElapsed - nowElapsed;
                 } else {
-                    timeoutMillis = latestStopTimeElapsed - nowElapsed;
+                    minTimeout = latestStopTimeElapsed - nowElapsed;
                 }
+                if (mAwaitingNotification) {
+                    minTimeout = Math.min(minTimeout, NOTIFICATION_TIMEOUT_MILLIS);
+                }
+                timeoutMillis = minTimeout;
                 break;
 
             case VERB_BINDING:
diff --git a/core/api/current.txt b/core/api/current.txt
index 81322e5..7e03b36 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2048,16 +2048,16 @@
     field public static final int system_primary_container_light;
     field public static final int system_primary_dark;
     field public static final int system_primary_fixed_dark;
-    field public static final int system_primary_fixed_darker_dark;
-    field public static final int system_primary_fixed_darker_light;
+    field public static final int system_primary_fixed_dim_dark;
+    field public static final int system_primary_fixed_dim_light;
     field public static final int system_primary_fixed_light;
     field public static final int system_primary_light;
     field public static final int system_secondary_container_dark;
     field public static final int system_secondary_container_light;
     field public static final int system_secondary_dark;
     field public static final int system_secondary_fixed_dark;
-    field public static final int system_secondary_fixed_darker_dark;
-    field public static final int system_secondary_fixed_darker_light;
+    field public static final int system_secondary_fixed_dim_dark;
+    field public static final int system_secondary_fixed_dim_light;
     field public static final int system_secondary_fixed_light;
     field public static final int system_secondary_light;
     field public static final int system_surface_bright_dark;
@@ -2082,8 +2082,8 @@
     field public static final int system_tertiary_container_light;
     field public static final int system_tertiary_dark;
     field public static final int system_tertiary_fixed_dark;
-    field public static final int system_tertiary_fixed_darker_dark;
-    field public static final int system_tertiary_fixed_darker_light;
+    field public static final int system_tertiary_fixed_dim_dark;
+    field public static final int system_tertiary_fixed_dim_light;
     field public static final int system_tertiary_fixed_light;
     field public static final int system_tertiary_light;
     field public static final int system_text_hint_inverse_dark;
@@ -10243,7 +10243,6 @@
     method @Deprecated public abstract int getWallpaperDesiredMinimumHeight();
     method @Deprecated public abstract int getWallpaperDesiredMinimumWidth();
     method public abstract void grantUriPermission(String, android.net.Uri, int);
-    method public boolean isDeviceContext();
     method public abstract boolean isDeviceProtectedStorage();
     method public boolean isRestricted();
     method public boolean isUiContext();
@@ -15445,8 +15444,9 @@
     ctor @Deprecated public EmbossMaskFilter(float[], float, float, float);
   }
 
-  public final class Gainmap {
+  public final class Gainmap implements android.os.Parcelable {
     ctor public Gainmap(@NonNull android.graphics.Bitmap);
+    method public int describeContents();
     method @NonNull public float getDisplayRatioForFullHdr();
     method @NonNull public float[] getEpsilonHdr();
     method @NonNull public float[] getEpsilonSdr();
@@ -15463,6 +15463,8 @@
     method @NonNull public void setMinDisplayRatioForHdrTransition(@FloatRange(from=1.0f) float);
     method @NonNull public void setRatioMax(float, float, float);
     method @NonNull public void setRatioMin(float, float, float);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.graphics.Gainmap> CREATOR;
   }
 
   public class HardwareBufferRenderer implements java.lang.AutoCloseable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2341971..fc6c1fe 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -463,6 +463,7 @@
     field public static final int config_systemAutomotiveCalendarSyncManager = 17039423; // 0x104003f
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
     field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
+    field public static final int config_systemCallStreaming;
     field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039
     field public static final int config_systemContacts = 17039403; // 0x104002b
     field public static final int config_systemFinancedDeviceController;
@@ -10054,7 +10055,7 @@
   public class SharedConnectivityManager {
     method public boolean connectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
     method public boolean connectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
-    method public boolean disconnectTetherNetwork();
+    method public boolean disconnectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
     method public boolean forgetKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
     method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
     method public boolean unregisterCallback(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
@@ -10141,7 +10142,7 @@
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onConnectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
     method public abstract void onConnectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
-    method public abstract void onDisconnectTetherNetwork();
+    method public abstract void onDisconnectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
     method public abstract void onForgetKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
     method public final void setKnownNetworks(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.KnownNetwork>);
     method public final void setSettingsState(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 4e364a2..d5ced04 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -627,6 +627,13 @@
     method @NonNull public android.app.admin.ResolutionMechanism<V> getResolutionMechanism();
   }
 
+  public abstract class PolicyUpdateReceiver extends android.content.BroadcastReceiver {
+    field public static final String EXTRA_POLICY_BUNDLE_KEY = "android.app.admin.extra.POLICY_BUNDLE_KEY";
+    field public static final String EXTRA_POLICY_KEY = "android.app.admin.extra.POLICY_KEY";
+    field public static final String EXTRA_POLICY_TARGET_USER_ID = "android.app.admin.extra.POLICY_TARGET_USER_ID";
+    field public static final String EXTRA_POLICY_UPDATE_RESULT_KEY = "android.app.admin.extra.POLICY_UPDATE_RESULT_KEY";
+  }
+
   public abstract class ResolutionMechanism<V> implements android.os.Parcelable {
   }
 
@@ -642,6 +649,13 @@
     field @NonNull public static final android.app.admin.StringSetUnion STRING_SET_UNION;
   }
 
+  public final class TargetUser {
+    field public static final int GLOBAL_USER_ID = -3; // 0xfffffffd
+    field public static final int LOCAL_USER_ID = -1; // 0xffffffff
+    field public static final int PARENT_USER_ID = -2; // 0xfffffffe
+    field public static final int UNKNOWN_USER_ID = -3; // 0xfffffffd
+  }
+
   public final class TopPriority<V> extends android.app.admin.ResolutionMechanism<V> {
     method public int describeContents();
     method @NonNull public java.util.List<android.app.admin.Authority> getHighestToLowestPriorityAuthorities();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index bed75db..e45944e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3068,18 +3068,6 @@
     }
 
     @Override
-    public boolean isDeviceContext() {
-        if (mIsExplicitDeviceId) {
-            if (mDeviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT) {
-                return true;
-            }
-            VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
-            return vdm.isValidVirtualDeviceId(mDeviceId);
-        }
-        return isAssociatedWithDisplay();
-    }
-
-    @Override
     public void registerDeviceIdChangeListener(@NonNull @CallbackExecutor Executor executor,
             @NonNull IntConsumer listener) {
         Objects.requireNonNull(executor, "executor cannot be null");
diff --git a/core/java/android/app/admin/PolicyUpdateReceiver.java b/core/java/android/app/admin/PolicyUpdateReceiver.java
index 58f4f1b..b5d9286 100644
--- a/core/java/android/app/admin/PolicyUpdateReceiver.java
+++ b/core/java/android/app/admin/PolicyUpdateReceiver.java
@@ -19,6 +19,7 @@
 import android.annotation.BroadcastBehavior;
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
+import android.annotation.TestApi;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -112,31 +113,37 @@
             "android.app.admin.extra.ACCOUNT_TYPE";
 
     /**
+     * String extra containing the policy identifier.
+     *
      * @hide
      */
-    public static final String EXTRA_POLICY_CHANGED_KEY =
-            "android.app.admin.extra.POLICY_CHANGED_KEY";
-
-    /**
-     * @hide
-     */
+    @TestApi
     public static final String EXTRA_POLICY_KEY = "android.app.admin.extra.POLICY_KEY";
 
     /**
+     * Bundle extra containing additional information related to a policy.
+     *
      * @hide
      */
+    @TestApi
     public static final String EXTRA_POLICY_BUNDLE_KEY =
             "android.app.admin.extra.POLICY_BUNDLE_KEY";
 
     /**
+     * Int extra containing the {@link PolicyUpdateResult} code.
+     *
      * @hide
      */
+    @TestApi
     public static final String EXTRA_POLICY_UPDATE_RESULT_KEY =
             "android.app.admin.extra.POLICY_UPDATE_RESULT_KEY";
 
     /**
+     * Int extra containing the target user this policy update applies to.
+     *
      * @hide
      */
+    @TestApi
     public static final String EXTRA_POLICY_TARGET_USER_ID =
             "android.app.admin.extra.POLICY_TARGET_USER_ID";
 
diff --git a/core/java/android/app/admin/TargetUser.java b/core/java/android/app/admin/TargetUser.java
index ed4fae0..ce6f32b 100644
--- a/core/java/android/app/admin/TargetUser.java
+++ b/core/java/android/app/admin/TargetUser.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 
 import java.util.Objects;
 
@@ -29,23 +30,35 @@
  */
 public final class TargetUser {
     /**
+     * Indicates that the policy relates to the user the admin is installed on.
+     *
      * @hide
      */
+    @TestApi
     public static final int LOCAL_USER_ID = -1;
 
     /**
+     * For admins of profiles, this indicates that the policy relates to the parent profile.
+     *
      * @hide
      */
+    @TestApi
     public static final int PARENT_USER_ID = -2;
 
     /**
+     * This indicates the policy is a global policy.
+     *
      * @hide
      */
+    @TestApi
     public static final int GLOBAL_USER_ID = -3;
 
     /**
+     * Indicates that the policy relates to some unknown user on the device.
+     *
      * @hide
      */
+    @TestApi
     public static final int UNKNOWN_USER_ID = -3;
 
     /**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0d330ce..fcffc6f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7384,7 +7384,6 @@
      * @throws IllegalArgumentException if the given device ID is not a valid ID of the default
      *         device or a virtual device.
      *
-     * @see #isDeviceContext()
      * @see #createDeviceContext(int)
      * @hide
      */
@@ -7408,26 +7407,8 @@
      * </p>
      *
      * <p>
-     * This method will only return a reliable value for this instance if
-     * {@link Context#isDeviceContext()} is {@code true}. The system can assign an arbitrary device
-     * id value for Contexts not logically associated with a device.
-     * </p>
-     *
-     * @return the ID of the device this context is associated with.
-     * @see #isDeviceContext()
-     * @see #createDeviceContext(int)
-     * @see #registerDeviceIdChangeListener(Executor, IntConsumer)
-     */
-    public int getDeviceId() {
-        throw new RuntimeException("Not implemented. Must override in a subclass.");
-    }
-
-    /**
-     * Indicates whether the value of {@link Context#getDeviceId()} can be relied upon for
-     * this instance. It will return {@code true} for Contexts created by
-     * {@link Context#createDeviceContext(int)} which reference a valid device ID, as well as for
-     * UI and Display Contexts.
-     * <p>
+     * This method will only return a reliable value for this instance if it was created with
+     * {@link Context#createDeviceContext(int)}, or if this instance is a UI or Display Context.
      * Contexts created with {@link Context#createDeviceContext(int)} will have an explicit
      * device association, which will never change, even if the underlying device is closed or is
      * removed. UI Contexts and Display Contexts are
@@ -7437,15 +7418,12 @@
      * logically associated with a device.
      * </p>
      *
-     * @return {@code true} if {@link Context#getDeviceId()} is reliable, {@code false} otherwise.
-     *
+     * @return the ID of the device this context is associated with.
      * @see #createDeviceContext(int)
-     * @see #getDeviceId()}
-     * @see #createDisplayContext(Display)
+     * @see #registerDeviceIdChangeListener(Executor, IntConsumer)
      * @see #isUiContext()
      */
-
-    public boolean isDeviceContext() {
+    public int getDeviceId() {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
 
@@ -7464,7 +7442,6 @@
      * @param listener The listener {@code IntConsumer} to call which will receive the updated
      *                 device ID.
      *
-     * @see Context#isDeviceContext()
      * @see Context#getDeviceId()
      * @see Context#createDeviceContext(int)
      */
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index a103128..91be664 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1201,11 +1201,6 @@
     }
 
     @Override
-    public boolean isDeviceContext() {
-        return mBase.isDeviceContext();
-    }
-
-    @Override
     public void registerDeviceIdChangeListener(@NonNull @CallbackExecutor Executor executor,
             @NonNull IntConsumer listener) {
         mBase.registerDeviceIdChangeListener(executor, listener);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index cfd291f..2b4ea70 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2914,10 +2914,10 @@
          *     <li>{@code requireUserAction} is set to {@link #USER_ACTION_NOT_REQUIRED}.</li>
          *     <li>The app being installed targets:
          *          <ul>
-         *              <li>{@link android.os.Build.VERSION_CODES#Q API 29} or higher on
-         *              Android S ({@link android.os.Build.VERSION_CODES#S API 31})</li>
-         *              <li>{@link android.os.Build.VERSION_CODES#R API 30} or higher after
-         *              Android S ({@link android.os.Build.VERSION_CODES#S API 31})</li>
+         *              <li>{@link android.os.Build.VERSION_CODES#R API 30} or higher on
+         *              Android T ({@link android.os.Build.VERSION_CODES#TIRAMISU API 33})</li>
+         *              <li>{@link android.os.Build.VERSION_CODES#S API 31} or higher <b>after</b>
+         *              Android T ({@link android.os.Build.VERSION_CODES#TIRAMISU API 33})</li>
          *          </ul>
          *     </li>
          *     <li>The installer is:
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 07473d2..ff7fc36 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -332,6 +332,7 @@
             @Nullable CancellationSignal cancellationSignal,
             @CallbackExecutor @NonNull Executor executor,
             @NonNull OutcomeReceiver<Void, ClearCredentialStateException> callback) {
+        requireNonNull(request, "request must not be null");
         requireNonNull(executor, "executor must not be null");
         requireNonNull(callback, "callback must not be null");
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 19e759ca..5fb699c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10323,7 +10323,7 @@
 
         /**
          * If active unlock triggers on unlock intents, then also request active unlock on
-         * these wake-up reasons. See PowerManager.WakeReason for value mappings.
+         * these wake-up reasons. See {@link PowerManager.WakeReason} for value mappings.
          * WakeReasons should be separated by a pipe. For example: "0|3" or "0". If this
          * setting should be disabled, then this should be set to an empty string. A null value
          * will use the system default value (WAKE_REASON_UNFOLD_DEVICE).
@@ -10333,6 +10333,17 @@
                 "active_unlock_wakeups_considered_unlock_intents";
 
         /**
+         * If active unlock triggers and succeeds on these wakeups, force dismiss keyguard on
+         * these wake reasons. See {@link PowerManager#WakeReason} for value mappings.
+         * WakeReasons should be separated by a pipe. For example: "0|3" or "0". If this
+         * setting should be disabled, then this should be set to an empty string. A null value
+         * will use the system default value (WAKE_REASON_UNFOLD_DEVICE).
+         * @hide
+         */
+        public static final String ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD =
+                "active_unlock_wakeups_to_force_dismiss_keyguard";
+
+        /**
          * Whether the assist gesture should be enabled.
          *
          * @hide
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
index b6c13c4..7e98bc7 100644
--- a/core/java/android/service/credentials/CredentialEntry.java
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -107,6 +107,7 @@
     }
 
     private CredentialEntry(@NonNull Parcel in) {
+        requireNonNull(in, "parcel must not be null");
         mType = in.readString8();
         mSlice = in.readTypedObject(Slice.CREATOR);
         mBeginGetCredentialOption = in.readTypedObject(BeginGetCredentialOption.CREATOR);
diff --git a/core/java/android/service/credentials/CredentialProviderInfo.java b/core/java/android/service/credentials/CredentialProviderInfo.java
index 75ba525..b5464db 100644
--- a/core/java/android/service/credentials/CredentialProviderInfo.java
+++ b/core/java/android/service/credentials/CredentialProviderInfo.java
@@ -16,6 +16,8 @@
 
 package android.service.credentials;
 
+import static java.util.Objects.requireNonNull;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -112,10 +114,9 @@
                         "Provider is not a valid system provider: " + serviceInfo);
             }
         }
-
         mIsSystemProvider = isSystemProvider;
-        mContext = context;
-        mServiceInfo = serviceInfo;
+        mContext =  requireNonNull(context, "context must not be null");
+        mServiceInfo = requireNonNull(serviceInfo, "serviceInfo must not be null");
         mCapabilities = new ArrayList<>();
         mIcon = mServiceInfo.loadIcon(mContext.getPackageManager());
         mLabel =
@@ -300,6 +301,7 @@
             @NonNull Context context,
             @UserIdInt int userId,
             boolean disableSystemAppVerificationForTests) {
+        requireNonNull(context, "context must not be null");
         final List<CredentialProviderInfo> providerInfos = new ArrayList<>();
         for (ServiceInfo si :
                 getAvailableSystemServiceInfos(
@@ -381,6 +383,8 @@
             int userId,
             boolean disableSystemAppVerificationForTests,
             int providerFilter) {
+        requireNonNull(context, "context must not be null");
+
         // Get the device policy.
         PackagePolicy pp = getDeviceManagerPolicy(context);
 
diff --git a/core/java/android/service/credentials/GetCredentialRequest.java b/core/java/android/service/credentials/GetCredentialRequest.java
index 7cdccc6..4f13922 100644
--- a/core/java/android/service/credentials/GetCredentialRequest.java
+++ b/core/java/android/service/credentials/GetCredentialRequest.java
@@ -23,6 +23,8 @@
 
 import com.android.internal.util.AnnotationValidations;
 
+import java.util.Objects;
+
 /**
  * Request for getting user's credential from a given credential provider.
  *
@@ -43,8 +45,10 @@
 
     public GetCredentialRequest(@NonNull CallingAppInfo callingAppInfo,
             @NonNull CredentialOption credentialOption) {
-        this.mCallingAppInfo = callingAppInfo;
-        this.mCredentialOption = credentialOption;
+        this.mCallingAppInfo = Objects.requireNonNull(callingAppInfo,
+                "callingAppInfo must not be null");
+        this.mCredentialOption = Objects.requireNonNull(credentialOption,
+                "credentialOption must not be null");
     }
 
     private GetCredentialRequest(@NonNull Parcel in) {
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 00161c0..d21e5df 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -60,7 +60,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -180,8 +179,6 @@
 
     private final ArrayList<Engine> mActiveEngines
             = new ArrayList<Engine>();
-    private Handler mBackgroundHandler;
-    private HandlerThread mBackgroundThread;
 
     static final class WallpaperCommand {
         String action;
@@ -201,6 +198,14 @@
      */
     public class Engine {
         IWallpaperEngineWrapper mIWallpaperEngine;
+        final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
+        final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
+
+        // 2D matrix [x][y] to represent a page of a portion of a window
+        EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
+        Bitmap mLastScreenshot;
+        int mLastWindowPage = -1;
+        private boolean mResetWindowPages;
 
         // Copies from mIWallpaperEngine.
         HandlerCaller mCaller;
@@ -261,27 +266,11 @@
 
         final Object mLock = new Object();
         boolean mOffsetMessageEnqueued;
-
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         float mPendingXOffset;
         float mPendingYOffset;
         float mPendingXOffsetStep;
         float mPendingYOffsetStep;
-
-        /**
-         * local color extraction related fields
-         * to be used by the background thread only (except the atomic boolean)
-         */
-        final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
-        final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
-        private long mLastProcessLocalColorsTimestamp;
-        private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
-        private int mPixelCopyCount = 0;
-        // 2D matrix [x][y] to represent a page of a portion of a window
-        EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
-        Bitmap mLastScreenshot;
-        private boolean mResetWindowPages;
-
         boolean mPendingSync;
         MotionEvent mPendingMove;
         boolean mIsInAmbientMode;
@@ -290,8 +279,12 @@
         private long mLastColorInvalidation;
         private final Runnable mNotifyColorsChanged = this::notifyColorsChanged;
 
+        // used to throttle processLocalColors
+        private long mLastProcessLocalColorsTimestamp;
+        private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
         private final Supplier<Long> mClockFunction;
         private final Handler mHandler;
+
         private Display mDisplay;
         private Context mDisplayContext;
         private int mDisplayState;
@@ -861,7 +854,7 @@
                             + "was not established.");
                 }
                 mResetWindowPages = true;
-                processLocalColors();
+                processLocalColors(mPendingXOffset, mPendingXOffsetStep);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e);
             }
@@ -1399,7 +1392,7 @@
                             resetWindowPages();
                             mSession.finishDrawing(mWindow, null /* postDrawTransaction */,
                                                    Integer.MAX_VALUE);
-                            processLocalColors();
+                            processLocalColors(mPendingXOffset, mPendingXOffsetStep);
                         }
                         reposition();
                         reportEngineShown(shouldWaitForEngineShown());
@@ -1543,7 +1536,7 @@
             if (!mDestroyed) {
                 mVisible = visible;
                 reportVisibility();
-                if (mReportedVisible) processLocalColors();
+                if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
             } else {
                 AnimationHandler.requestAnimatorsEnabled(visible, this);
             }
@@ -1627,44 +1620,31 @@
             }
 
             // setup local color extraction data
-            processLocalColors();
+            processLocalColors(xOffset, xOffsetStep);
         }
 
         /**
          * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of
          * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls.
          */
-        private void processLocalColors() {
+        private void processLocalColors(float xOffset, float xOffsetStep) {
             if (mProcessLocalColorsPending.compareAndSet(false, true)) {
                 final long now = mClockFunction.get();
                 final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp;
                 final long timeToWait = Math.max(0,
                         PROCESS_LOCAL_COLORS_INTERVAL_MS - timeSinceLastColorProcess);
 
-                mBackgroundHandler.postDelayed(() -> {
+                mHandler.postDelayed(() -> {
                     mLastProcessLocalColorsTimestamp = now + timeToWait;
                     mProcessLocalColorsPending.set(false);
-                    processLocalColorsInternal();
+                    processLocalColorsInternal(xOffset, xOffsetStep);
                 }, timeToWait);
             }
         }
 
-        private void processLocalColorsInternal() {
+        private void processLocalColorsInternal(float xOffset, float xOffsetStep) {
             // implemented by the wallpaper
             if (supportsLocalColorExtraction()) return;
-            if (!mBackgroundHandler.getLooper().isCurrentThread()) {
-                throw new IllegalStateException(
-                        "ProcessLocalColors should be called from the background thread");
-            }
-            float xOffset;
-            float xOffsetStep;
-            float wallpaperDimAmount;
-            synchronized (mLock) {
-                xOffset = mPendingXOffset;
-                xOffsetStep = mPendingXOffsetStep;
-                wallpaperDimAmount = mWallpaperDimAmount;
-            }
-
             if (DEBUG) {
                 Log.d(TAG, "processLocalColors " + xOffset + " of step "
                         + xOffsetStep);
@@ -1727,7 +1707,7 @@
                 xPage = mWindowPages.length - 1;
             }
             current = mWindowPages[xPage];
-            updatePage(current, xPage, xPages, wallpaperDimAmount);
+            updatePage(current, xPage, xPages, finalXOffsetStep);
             Trace.endSection();
         }
 
@@ -1747,21 +1727,16 @@
             }
         }
 
-        /**
-         * Must be called with the surface lock held.
-         * Must not be called if the surface is not valid.
-         * Will unlock the surface when done using it.
-         */
         void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages,
-                float wallpaperDimAmount) {
-
+                float xOffsetStep) {
             // in case the clock is zero, we start with negative time
             long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION;
             long lapsed = current - currentPage.getLastUpdateTime();
             // Always update the page when the last update time is <= 0
             // This is important especially when the device first boots
-            if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return;
-
+            if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) {
+                return;
+            }
             Surface surface = mSurfaceHolder.getSurface();
             if (!surface.isValid()) return;
             boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y;
@@ -1777,41 +1752,32 @@
             Bitmap screenShot = Bitmap.createBitmap(width, height,
                     Bitmap.Config.ARGB_8888);
             final Bitmap finalScreenShot = screenShot;
-            final String pixelCopySectionName = "WallpaperService#pixelCopy";
-            final int pixelCopyCount = mPixelCopyCount++;
-            Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount);
-            try {
-                PixelCopy.request(surface, screenShot, (res) -> {
-                    mSurfaceHolder.mSurfaceLock.unlock();
-                    Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount);
-                    if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
-                    if (res != PixelCopy.SUCCESS) {
-                        Bitmap lastBitmap = currentPage.getBitmap();
-                        // assign the last bitmap taken for now
-                        currentPage.setBitmap(mLastScreenshot);
-                        Bitmap lastScreenshot = mLastScreenshot;
-                        if (lastScreenshot != null && !lastScreenshot.isRecycled()
-                                && !Objects.equals(lastBitmap, lastScreenshot)) {
-                            updatePageColors(currentPage, pageIndx, numPages, wallpaperDimAmount);
-                        }
-                    } else {
-                        mLastScreenshot = finalScreenShot;
-                        // going to hold this lock for a while
-                        currentPage.setBitmap(finalScreenShot);
-                        currentPage.setLastUpdateTime(current);
-                        updatePageColors(currentPage, pageIndx, numPages, wallpaperDimAmount);
+            Trace.beginSection("WallpaperService#pixelCopy");
+            PixelCopy.request(surface, screenShot, (res) -> {
+                Trace.endSection();
+                if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
+                if (res != PixelCopy.SUCCESS) {
+                    Bitmap lastBitmap = currentPage.getBitmap();
+                    // assign the last bitmap taken for now
+                    currentPage.setBitmap(mLastScreenshot);
+                    Bitmap lastScreenshot = mLastScreenshot;
+                    if (lastScreenshot != null && !lastScreenshot.isRecycled()
+                            && !Objects.equals(lastBitmap, lastScreenshot)) {
+                        updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
                     }
-                }, mBackgroundHandler);
-            } catch (IllegalArgumentException e) {
-                // this can potentially happen if the surface is invalidated right between the
-                // surface.isValid() check and the PixelCopy operation.
-                // in this case, stop: we'll compute colors on the next processLocalColors call.
-                Log.i(TAG, "Cancelling processLocalColors: exception caught during PixelCopy");
-            }
+                } else {
+                    mLastScreenshot = finalScreenShot;
+                    // going to hold this lock for a while
+                    currentPage.setBitmap(finalScreenShot);
+                    currentPage.setLastUpdateTime(current);
+                    updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
+                }
+            }, mHandler);
+
         }
         // locked by the passed page
-        private void updatePageColors(
-                EngineWindowPage page, int pageIndx, int numPages, float wallpaperDimAmount) {
+        private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages,
+                float xOffsetStep) {
             if (page.getBitmap() == null) return;
             Trace.beginSection("WallpaperService#updatePageColors");
             if (DEBUG) {
@@ -1834,7 +1800,7 @@
                     Log.e(TAG, "Error creating page local color bitmap", e);
                     continue;
                 }
-                WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount);
+                WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount);
                 target.recycle();
                 WallpaperColors currentColor = page.getColors(area);
 
@@ -1851,14 +1817,12 @@
                                 + " local color callback for area" + area + " for page " + pageIndx
                                 + " of " + numPages);
                     }
-                    mHandler.post(() -> {
-                        try {
-                            mConnection.onLocalWallpaperColorsChanged(area, color,
-                                    mDisplayContext.getDisplayId());
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
-                        }
-                    });
+                    try {
+                        mConnection.onLocalWallpaperColorsChanged(area, color,
+                                mDisplayContext.getDisplayId());
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
+                    }
                 }
             }
             Trace.endSection();
@@ -1888,6 +1852,7 @@
             if (supportsLocalColorExtraction()) return;
             if (!mResetWindowPages) return;
             mResetWindowPages = false;
+            mLastWindowPage = -1;
             for (int i = 0; i < mWindowPages.length; i++) {
                 mWindowPages[i].setLastUpdateTime(0L);
             }
@@ -1913,10 +1878,12 @@
             if (DEBUG) {
                 Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions);
             }
-            mBackgroundHandler.post(() -> {
+            mHandler.post(() -> {
                 mLocalColorsToAdd.addAll(regions);
-                processLocalColors();
+                processLocalColors(mPendingXOffset, mPendingYOffset);
             });
+
+
         }
 
         /**
@@ -1926,7 +1893,7 @@
          */
         public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
             if (supportsLocalColorExtraction()) return;
-            mBackgroundHandler.post(() -> {
+            mHandler.post(() -> {
                 float step = mPendingXOffsetStep;
                 mLocalColorsToAdd.removeAll(regions);
                 mLocalColorAreas.removeAll(regions);
@@ -2583,10 +2550,6 @@
     @Override
     public void onCreate() {
         Trace.beginSection("WPMS.onCreate");
-        final String localColorExtractorThreadName = "DefaultWallpaperLocalColorExtractor";
-        mBackgroundThread = new HandlerThread(localColorExtractorThreadName);
-        mBackgroundThread.start();
-        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
         super.onCreate();
         Trace.endSection();
     }
@@ -2599,7 +2562,6 @@
             mActiveEngines.get(i).detach();
         }
         mActiveEngines.clear();
-        mBackgroundThread.quitSafely();
         Trace.endSection();
     }
 
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 18fdb83..f6309f2 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -32,6 +32,8 @@
 import android.telephony.Annotation.SimActivationState;
 import android.telephony.Annotation.SrvccState;
 import android.telephony.TelephonyManager.DataEnabledReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeStopReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeType;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.MediaQualityStatus;
@@ -1304,8 +1306,6 @@
         // default implementation empty
     }
 
-
-
     /**
      * The callback methods need to be called on the handler thread where
      * this object was created.  If the binder did that for us it'd be nice.
@@ -1669,6 +1669,18 @@
         public final void onMediaQualityStatusChanged(MediaQualityStatus mediaQualityStatus) {
             // not support. Can't override. Use TelephonyCallback.
         }
+
+        /** @hide */
+        public final void onCallBackModeStarted(
+                @TelephonyManager.EmergencyCallbackModeType int type) {
+            // not support. Can't override. Use TelephonyCallback.
+        }
+
+        /** @hide */
+        public final void onCallBackModeStopped(@EmergencyCallbackModeType int type,
+                @EmergencyCallbackModeStopReason int reason) {
+            // not support. Can't override. Use TelephonyCallback.
+        }
     }
 
     private void log(String s) {
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index f8df668..7ada058 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -612,6 +612,20 @@
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public static final int EVENT_MEDIA_QUALITY_STATUS_CHANGED = 39;
 
+
+    /**
+     * Event for changes to the Emergency callback mode
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @see EmergencyCallbackModeListener#onCallbackModeStarted(int)
+     * @see EmergencyCallbackModeListener#onCallbackModeStopped(int, int)
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_EMERGENCY_CALLBACK_MODE_CHANGED = 40;
+
     /**
      * @hide
      */
@@ -654,7 +668,8 @@
             EVENT_LEGACY_CALL_STATE_CHANGED,
             EVENT_LINK_CAPACITY_ESTIMATE_CHANGED,
             EVENT_TRIGGER_NOTIFY_ANBR,
-            EVENT_MEDIA_QUALITY_STATUS_CHANGED
+            EVENT_MEDIA_QUALITY_STATUS_CHANGED,
+            EVENT_EMERGENCY_CALLBACK_MODE_CHANGED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface TelephonyEvent {
@@ -1568,6 +1583,53 @@
     }
 
     /**
+     * Interface for emergency callback mode listener.
+     *
+     * @hide
+     */
+    public interface EmergencyCallbackModeListener {
+        /**
+         * Indicates that Callback Mode has been started.
+         * <p>
+         * This method will be called when an emergency sms/emergency call is sent
+         * and the callback mode is supported by the carrier.
+         * If an emergency SMS is transmitted during callback mode for SMS, this API will be called
+         * once again with TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS.
+         *
+         * @param type for callback mode entry
+         *             See {@link TelephonyManager.EmergencyCallbackModeType}.
+         * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL
+         * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onCallBackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type);
+
+        /**
+         * Indicates that Callback Mode has been stopped.
+         * <p>
+         * This method will be called when the callback mode timer expires or when
+         * a normal call/SMS is sent
+         *
+         * @param type for callback mode entry
+         * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL
+         * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS
+         *
+         * @param reason for changing callback mode
+         *
+         * @see TelephonyManager#STOP_REASON_UNKNOWN
+         * @see TelephonyManager#STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED
+         * @see TelephonyManager#STOP_REASON_NORMAL_SMS_SENT
+         * @see TelephonyManager#STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED
+         * @see TelephonyManager#STOP_REASON_EMERGENCY_SMS_SENT
+         * @see TelephonyManager#STOP_REASON_TIMER_EXPIRED
+         * @see TelephonyManager#STOP_REASON_USER_ACTION
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onCallBackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type,
+                @TelephonyManager.EmergencyCallbackModeStopReason int reason);
+    }
+
+    /**
      * The callback methods need to be called on the handler thread where
      * this object was created.  If the binder did that for us it'd be nice.
      * <p>
@@ -1935,5 +1997,27 @@
                     () -> mExecutor.execute(() -> listener.onMediaQualityStatusChanged(
                             mediaQualityStatus)));
         }
+
+        public void onCallBackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type) {
+            EmergencyCallbackModeListener listener =
+                    (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get();
+            Log.d(LOG_TAG, "onCallBackModeStarted:type=" + type + ", listener=" + listener);
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallBackModeStarted(type)));
+        }
+
+        public void onCallBackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type,
+                @TelephonyManager.EmergencyCallbackModeStopReason int reason) {
+            EmergencyCallbackModeListener listener =
+                    (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get();
+            Log.d(LOG_TAG, "onCallBackModeStopped:type=" + type
+                    + ", reason=" + reason + ", listener=" + listener);
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallBackModeStopped(type, reason)));
+        }
     }
 }
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 8b24e07..f648ad4 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -1113,6 +1113,11 @@
             eventList.add(TelephonyCallback.EVENT_MEDIA_QUALITY_STATUS_CHANGED);
         }
 
+        if (telephonyCallback instanceof TelephonyCallback.EmergencyCallbackModeListener) {
+            eventList.add(TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED);
+        }
+
+
         return eventList;
     }
 
@@ -1539,4 +1544,43 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Notify Callback Mode has been started.
+     * @param phoneId Sender phone ID.
+     * @param subId Sender subscription ID.
+     * @param type for callback mode entry.
+     *             See {@link TelephonyManager.EmergencyCallbackModeType}.
+     */
+    public void notifyCallBackModeStarted(int phoneId, int subId,
+            @TelephonyManager.EmergencyCallbackModeType int type) {
+        try {
+            Log.d(TAG, "notifyCallBackModeStarted:type=" + type);
+            sRegistry.notifyCallbackModeStarted(phoneId, subId, type);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify Callback Mode has been stopped.
+     * @param phoneId Sender phone ID.
+     * @param subId Sender subscription ID.
+     * @param type for callback mode entry.
+     *             See {@link TelephonyManager.EmergencyCallbackModeType}.
+     * @param reason for changing callback mode.
+     *             See {@link TelephonyManager.EmergencyCallbackModeStopReason}.
+     */
+    public void notifyCallbackModeStopped(int phoneId, int subId,
+            @TelephonyManager.EmergencyCallbackModeType int type,
+            @TelephonyManager.EmergencyCallbackModeStopReason int reason) {
+        try {
+            Log.d(TAG, "notifyCallbackModeStopped:type=" + type + ", reason=" + reason);
+            sRegistry.notifyCallbackModeStopped(phoneId, subId, type, reason);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c2afd20..7d18bf0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10199,6 +10199,7 @@
             setNotifiedContentCaptureAppeared();
 
             if (ai != null) {
+                makeParentImportantAndNotifyAppearedEventIfNeed();
                 ai.delayNotifyContentCaptureEvent(session, this, appeared);
             } else {
                 if (DEBUG_CONTENT_CAPTURE) {
@@ -10226,6 +10227,22 @@
         }
     }
 
+    private void makeParentImportantAndNotifyAppearedEventIfNeed() {
+        // If view sent the appeared event to Content Capture, Content Capture also
+        // would like to receive its parents' appeared events. So checks its parents
+        // whether the appeared event is sent or not. If not, send the appeared event.
+        final ViewParent parent = getParent();
+        if (parent instanceof View) {
+            View p = ((View) parent);
+            if (p.getNotifiedContentCaptureAppeared()) {
+                return;
+            }
+            // Set important for content capture in the cache.
+            p.mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
+            p.notifyAppearedOrDisappearedForContentCaptureIfNeeded(/* appeared */ true);
+        }
+    }
+
     private void setNotifiedContentCaptureAppeared() {
         mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
         mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 63785f2..134a917 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -139,15 +139,6 @@
     }
 
     @Override
-    public boolean isDeviceContext() {
-        Context context = mContext.get();
-        if (context != null) {
-            return context.isDeviceContext();
-        }
-        return false;
-    }
-
-    @Override
     public boolean isConfigurationContext() {
         Context context = mContext.get();
         if (context != null) {
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 3e9f1cb..03cfd4f 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -78,4 +78,6 @@
     void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType);
     void onLinkCapacityEstimateChanged(in List<LinkCapacityEstimate> linkCapacityEstimateList);
     void onMediaQualityStatusChanged(in MediaQualityStatus mediaQualityStatus);
+    void onCallBackModeStarted(int type);
+    void onCallBackModeStopped(int type, int reason);
 }
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index fd9239d..8a02dd6 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -117,4 +117,7 @@
             String pkg, String featureId);
     void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener, String pkg);
     void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId, int specificCarrierId);
+
+    void notifyCallbackModeStarted(int phoneId, int subId, int type);
+    void notifyCallbackModeStopped(int phoneId, int subId, int type, int reason);
 }
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 1910331..b1dab85 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -18,29 +18,25 @@
 
 #define LOG_TAG "AudioRecord-JNI"
 
-#include <inttypes.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-
-#include <utils/Log.h>
-#include <media/AudioRecord.h>
-#include <media/MicrophoneInfo.h>
-#include <vector>
-
 #include <android/content/AttributionSourceState.h>
 #include <android_os_Parcel.h>
-
+#include <inttypes.h>
+#include <jni.h>
+#include <media/AudioRecord.h>
+#include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
+#include <utils/Log.h>
+
+#include <vector>
 
 #include "android_media_AudioAttributes.h"
-#include "android_media_AudioFormat.h"
 #include "android_media_AudioErrors.h"
+#include "android_media_AudioFormat.h"
 #include "android_media_DeviceCallback.h"
 #include "android_media_JNIUtils.h"
 #include "android_media_MediaMetricsJNI.h"
 #include "android_media_MicrophoneInfo.h"
-
+#include "core_jni_helpers.h"
 
 // ----------------------------------------------------------------------------
 
@@ -719,7 +715,7 @@
     }
 
     jint jStatus = AUDIO_JAVA_SUCCESS;
-    std::vector<media::MicrophoneInfo> activeMicrophones;
+    std::vector<media::MicrophoneInfoFw> activeMicrophones;
     status_t status = lpRecorder->getActiveMicrophones(&activeMicrophones);
     if (status != NO_ERROR) {
         ALOGE_IF(status != NO_ERROR, "AudioRecord::getActiveMicrophones error %d", status);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 75f8402..8ba4eed 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -28,7 +28,6 @@
 #include <media/AudioContainers.h>
 #include <media/AudioPolicy.h>
 #include <media/AudioSystem.h>
-#include <media/MicrophoneInfo.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <system/audio.h>
@@ -2424,7 +2423,7 @@
     }
 
     jint jStatus;
-    std::vector<media::MicrophoneInfo> microphones;
+    std::vector<media::MicrophoneInfoFw> microphones;
     status_t status = AudioSystem::getMicrophones(&microphones);
     if (status != NO_ERROR) {
         ALOGE("AudioSystem::getMicrophones error %d", status);
diff --git a/core/jni/android_media_MicrophoneInfo.cpp b/core/jni/android_media_MicrophoneInfo.cpp
index b70190f..65e30d8 100644
--- a/core/jni/android_media_MicrophoneInfo.cpp
+++ b/core/jni/android_media_MicrophoneInfo.cpp
@@ -15,6 +15,9 @@
  */
 
 #include "android_media_MicrophoneInfo.h"
+
+#include <media/AidlConversion.h>
+
 #include "android_media_AudioErrors.h"
 #include "core_jni_helpers.h"
 
@@ -46,8 +49,17 @@
 namespace android {
 
 jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo,
-        const media::MicrophoneInfo *microphoneInfo)
-{
+                                     const media::MicrophoneInfoFw *microphoneInfo) {
+    // The Java object uses the same enum values as the C enum values, which are
+    // generated from HIDL. Thus, we can use the legacy structure as the source for
+    // creating the Java object. Once we start removing legacy types, we can add
+    // direct converters between Java and AIDL, this will eliminate the need
+    // to have JNI code like this one.
+    auto conv = aidl2legacy_MicrophoneInfoFw_audio_microphone_characteristic_t(*microphoneInfo);
+    if (!conv.ok()) {
+        return nativeToJavaStatus(conv.error());
+    }
+
     jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
     jstring jDeviceId = NULL;
     jstring jAddress = NULL;
@@ -56,36 +68,23 @@
     jobject jFrequencyResponses = NULL;
     jobject jChannelMappings = NULL;
 
-    jDeviceId = env->NewStringUTF(microphoneInfo->getDeviceId().c_str());
-    jAddress = env->NewStringUTF(microphoneInfo->getAddress().c_str());
-    if (microphoneInfo->getGeometricLocation().size() != 3 ||
-            microphoneInfo->getOrientation().size() != 3) {
-        jStatus = nativeToJavaStatus(BAD_VALUE);
-        goto exit;
-    }
-    jGeometricLocation = env->NewObject(gMicrophoneInfoCoordinateClass,
-                                        gMicrophoneInfoCoordinateCstor,
-                                        microphoneInfo->getGeometricLocation()[0],
-                                        microphoneInfo->getGeometricLocation()[1],
-                                        microphoneInfo->getGeometricLocation()[2]);
-    jOrientation = env->NewObject(gMicrophoneInfoCoordinateClass,
-                                  gMicrophoneInfoCoordinateCstor,
-                                  microphoneInfo->getOrientation()[0],
-                                  microphoneInfo->getOrientation()[1],
-                                  microphoneInfo->getOrientation()[2]);
+    const auto &micInfo = conv.value();
+    jDeviceId = env->NewStringUTF(micInfo.device_id);
+    jAddress = env->NewStringUTF(micInfo.address);
+    jGeometricLocation =
+            env->NewObject(gMicrophoneInfoCoordinateClass, gMicrophoneInfoCoordinateCstor,
+                           micInfo.geometric_location.x, micInfo.geometric_location.y,
+                           micInfo.geometric_location.z);
+    jOrientation =
+            env->NewObject(gMicrophoneInfoCoordinateClass, gMicrophoneInfoCoordinateCstor,
+                           micInfo.orientation.x, micInfo.orientation.y, micInfo.orientation.z);
     // Create a list of Pair for frequency response.
-    if (microphoneInfo->getFrequencyResponses().size() != 2 ||
-            microphoneInfo->getFrequencyResponses()[0].size() !=
-                    microphoneInfo->getFrequencyResponses()[1].size()) {
-        jStatus = nativeToJavaStatus(BAD_VALUE);
-        goto exit;
-    }
     jFrequencyResponses = env->NewObject(gArrayListClass, gArrayListCstor);
-    for (size_t i = 0; i < microphoneInfo->getFrequencyResponses()[0].size(); i++) {
-        jobject jFrequency = env->NewObject(gFloatClass, gFloatCstor,
-                                            microphoneInfo->getFrequencyResponses()[0][i]);
-        jobject jResponse = env->NewObject(gFloatClass, gFloatCstor,
-                                          microphoneInfo->getFrequencyResponses()[1][i]);
+    for (size_t i = 0; i < micInfo.num_frequency_responses; i++) {
+        jobject jFrequency =
+                env->NewObject(gFloatClass, gFloatCstor, micInfo.frequency_responses[0][i]);
+        jobject jResponse =
+                env->NewObject(gFloatClass, gFloatCstor, micInfo.frequency_responses[1][i]);
         jobject jFrequencyResponse = env->NewObject(gPairClass, gPairCstor, jFrequency, jResponse);
         env->CallBooleanMethod(jFrequencyResponses, gArrayListMethods.add, jFrequencyResponse);
         env->DeleteLocalRef(jFrequency);
@@ -93,13 +92,9 @@
         env->DeleteLocalRef(jFrequencyResponse);
     }
     // Create a list of Pair for channel mapping.
-    if (microphoneInfo->getChannelMapping().size() != AUDIO_CHANNEL_COUNT_MAX) {
-        jStatus = nativeToJavaStatus(BAD_VALUE);
-        goto exit;
-    }
-    jChannelMappings = env->NewObject(gArrayListClass, gArrayListCstor);
-    for (size_t i = 0; i < microphoneInfo->getChannelMapping().size(); i++) {
-        int channelMappingType = microphoneInfo->getChannelMapping()[i];
+    const auto &channelMapping = micInfo.channel_mapping;
+    for (size_t i = 0; i < std::size(channelMapping); i++) {
+        int channelMappingType = channelMapping[i];
         if (channelMappingType != AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED) {
             jobject jChannelIndex = env->NewObject(gIntegerClass, gIntegerCstor, i);
             jobject jChannelMappingType = env->NewObject(gIntegerClass, gIntegerCstor,
@@ -113,18 +108,11 @@
         }
     }
     *jMicrophoneInfo = env->NewObject(gMicrophoneInfoClass, gMicrophoneInfoCstor, jDeviceId,
-                                      microphoneInfo->getType(), jAddress,
-                                      microphoneInfo->getDeviceLocation(),
-                                      microphoneInfo->getDeviceGroup(),
-                                      microphoneInfo->getIndexInTheGroup(),
-                                      jGeometricLocation, jOrientation,
-                                      jFrequencyResponses, jChannelMappings,
-                                      microphoneInfo->getSensitivity(),
-                                      microphoneInfo->getMaxSpl(),
-                                      microphoneInfo->getMinSpl(),
-                                      microphoneInfo->getDirectionality());
+                                      micInfo.device, jAddress, micInfo.location, micInfo.group,
+                                      micInfo.index_in_the_group, jGeometricLocation, jOrientation,
+                                      jFrequencyResponses, jChannelMappings, micInfo.sensitivity,
+                                      micInfo.max_spl, micInfo.min_spl, micInfo.directionality);
 
-exit:
     if (jDeviceId != NULL) {
         env->DeleteLocalRef(jDeviceId);
     }
@@ -145,7 +133,6 @@
     }
     return jStatus;
 }
-
 }
 
 int register_android_media_MicrophoneInfo(JNIEnv *env)
diff --git a/core/jni/android_media_MicrophoneInfo.h b/core/jni/android_media_MicrophoneInfo.h
index 241b6d0..dc531c6 100644
--- a/core/jni/android_media_MicrophoneInfo.h
+++ b/core/jni/android_media_MicrophoneInfo.h
@@ -17,8 +17,8 @@
 #ifndef ANDROID_MEDIA_MICROPHONEINFO_H
 #define ANDROID_MEDIA_MICROPHONEINFO_H
 
+#include <android/media/MicrophoneInfoFw.h>
 #include <system/audio.h>
-#include <media/MicrophoneInfo.h>
 
 #include "jni.h"
 
@@ -27,7 +27,7 @@
 // Conversion from C++ MicrophoneInfo object to Java MicrophoneInfo object
 
 extern jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo,
-        const media::MicrophoneInfo *microphoneInfo);
+                                            const media::MicrophoneInfoFw *microphoneInfo);
 } // namespace android
 
-#endif
\ No newline at end of file
+#endif
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a4818c7..2376baa 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5838,7 +5838,7 @@
          <p>Not for use by third-party applications.</p>
          @hide -->
     <permission android:name="android.permission.CALL_AUDIO_INTERCEPTION"
-                android:protectionLevel="signature|privileged" />
+                android:protectionLevel="signature|privileged|role" />
 
     <!-- @TestApi Allows an application to query audio related state.
          @hide -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 0c2fc1d..68c8d41 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -470,15 +470,15 @@
     <color name="system_error_container_light">#FFDAD5</color>
     <color name="system_on_error_container_light">#410000</color>
     <color name="system_primary_fixed_light">#D8E2FF</color>
-    <color name="system_primary_fixed_darker_light">#ADC6FF</color>
+    <color name="system_primary_fixed_dim_light">#ADC6FF</color>
     <color name="system_on_primary_fixed_light">#001A41</color>
     <color name="system_on_primary_fixed_variant_light">#2B4678</color>
     <color name="system_secondary_fixed_light">#DBE2F9</color>
-    <color name="system_secondary_fixed_darker_light">#BFC6DC</color>
+    <color name="system_secondary_fixed_dim_light">#BFC6DC</color>
     <color name="system_on_secondary_fixed_light">#141B2C</color>
     <color name="system_on_secondary_fixed_variant_light">#3F4759</color>
     <color name="system_tertiary_fixed_light">#FBD7FC</color>
-    <color name="system_tertiary_fixed_darker_light">#DEBCDF</color>
+    <color name="system_tertiary_fixed_dim_light">#DEBCDF</color>
     <color name="system_on_tertiary_fixed_light">#29132D</color>
     <color name="system_on_tertiary_fixed_variant_light">#583E5B</color>
     <color name="system_control_activated_light">#D8E2FF</color>
@@ -525,15 +525,15 @@
     <color name="system_error_container_dark">#930001</color>
     <color name="system_on_error_container_dark">#FFDAD5</color>
     <color name="system_primary_fixed_dark">#D8E2FF</color>
-    <color name="system_primary_fixed_darker_dark">#ADC6FF</color>
+    <color name="system_primary_fixed_dim_dark">#ADC6FF</color>
     <color name="system_on_primary_fixed_dark">#001A41</color>
     <color name="system_on_primary_fixed_variant_dark">#2B4678</color>
     <color name="system_secondary_fixed_dark">#DBE2F9</color>
-    <color name="system_secondary_fixed_darker_dark">#BFC6DC</color>
+    <color name="system_secondary_fixed_dim_dark">#BFC6DC</color>
     <color name="system_on_secondary_fixed_dark">#141B2C</color>
     <color name="system_on_secondary_fixed_variant_dark">#3F4759</color>
     <color name="system_tertiary_fixed_dark">#FBD7FC</color>
-    <color name="system_tertiary_fixed_darker_dark">#DEBCDF</color>
+    <color name="system_tertiary_fixed_dim_dark">#DEBCDF</color>
     <color name="system_on_tertiary_fixed_dark">#29132D</color>
     <color name="system_on_tertiary_fixed_variant_dark">#583E5B</color>
     <color name="system_control_activated_dark">#2B4678</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8e5ae9c..81d095a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2189,6 +2189,8 @@
     <bool name="config_enableDefaultNotesForWorkProfile">false</bool>
     <!-- The name of the package that will hold the system financed device controller role. -->
     <string name="config_systemFinancedDeviceController" translatable="false">com.android.devicelockcontroller</string>
+    <!-- The name of the package that will hold the call streaming role. -->
+    <string name="config_systemCallStreaming" translatable="false"></string>
 
     <!-- The component name of the wear service class that will be started by the system server. -->
     <string name="config_wearServiceComponent" translatable="false"></string>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 7d41f76..b23d5b4 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -148,6 +148,8 @@
     <public name="config_defaultNotes" />
     <!-- @hide @SystemApi -->
     <public name="config_systemFinancedDeviceController" />
+    <!-- @hide @SystemApi -->
+    <public name="config_systemCallStreaming" />
   </staging-public-group>
 
   <staging-public-group type="dimen" first-id="0x01ca0000">
@@ -187,15 +189,15 @@
     <public name="system_error_container_light" />
     <public name="system_on_error_container_light" />
     <public name="system_primary_fixed_light" />
-    <public name="system_primary_fixed_darker_light" />
+    <public name="system_primary_fixed_dim_light" />
     <public name="system_on_primary_fixed_light" />
     <public name="system_on_primary_fixed_variant_light" />
     <public name="system_secondary_fixed_light" />
-    <public name="system_secondary_fixed_darker_light" />
+    <public name="system_secondary_fixed_dim_light" />
     <public name="system_on_secondary_fixed_light" />
     <public name="system_on_secondary_fixed_variant_light" />
     <public name="system_tertiary_fixed_light" />
-    <public name="system_tertiary_fixed_darker_light" />
+    <public name="system_tertiary_fixed_dim_light" />
     <public name="system_on_tertiary_fixed_light" />
     <public name="system_on_tertiary_fixed_variant_light" />
     <public name="system_control_activated_light" />
@@ -242,15 +244,15 @@
     <public name="system_error_container_dark"/>
     <public name="system_on_error_container_dark"/>
     <public name="system_primary_fixed_dark"/>
-    <public name="system_primary_fixed_darker_dark"/>
+    <public name="system_primary_fixed_dim_dark"/>
     <public name="system_on_primary_fixed_dark"/>
     <public name="system_on_primary_fixed_variant_dark"/>
     <public name="system_secondary_fixed_dark"/>
-    <public name="system_secondary_fixed_darker_dark"/>
+    <public name="system_secondary_fixed_dim_dark"/>
     <public name="system_on_secondary_fixed_dark"/>
     <public name="system_on_secondary_fixed_variant_dark"/>
     <public name="system_tertiary_fixed_dark"/>
-    <public name="system_tertiary_fixed_darker_dark"/>
+    <public name="system_tertiary_fixed_dim_dark"/>
     <public name="system_on_tertiary_fixed_dark"/>
     <public name="system_on_tertiary_fixed_variant_dark"/>
     <public name="system_control_activated_dark"/>
diff --git a/core/tests/coretests/src/android/credentials/CredentialManagerTest.java b/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
new file mode 100644
index 0000000..444e9f2
--- /dev/null
+++ b/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.slice.Slice;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.service.credentials.CredentialEntry;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CredentialManagerTest {
+    @Mock
+    private ICredentialManager mMockCredentialManagerService;
+
+    @Mock
+    private Activity mMockActivity;
+
+    private GetCredentialRequest mGetRequest;
+    private CreateCredentialRequest mCreateRequest;
+
+    private ClearCredentialStateRequest mClearRequest;
+    private RegisterCredentialDescriptionRequest mRegisterRequest;
+    private UnregisterCredentialDescriptionRequest mUnregisterRequest;
+
+    private CredentialManager mCredentialManager;
+    private Executor mExecutor;
+    private String mPackageName;
+
+    private static boolean bundleEquals(Bundle a, Bundle b) {
+        if (a == null && b == null) {
+            return true;
+        }
+        if (a == null || b == null) {
+            return false;
+        }
+        for (String aKey : a.keySet()) {
+            if (!Objects.equals(a.get(aKey), b.get(aKey))) {
+                return false;
+            }
+        }
+
+        for (String bKey : b.keySet()) {
+            if (!Objects.equals(b.get(bKey), a.get(bKey))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static void assertBundleEquals(Bundle a, Bundle b) {
+        assertThat(bundleEquals(a, b)).isTrue();
+    }
+
+    @Before
+    public void setup() {
+        mGetRequest = new GetCredentialRequest.Builder(Bundle.EMPTY).addCredentialOption(
+                new CredentialOption(Credential.TYPE_PASSWORD_CREDENTIAL, Bundle.EMPTY,
+                        Bundle.EMPTY, false)).build();
+        mCreateRequest = new CreateCredentialRequest(Credential.TYPE_PASSWORD_CREDENTIAL,
+                Bundle.EMPTY, Bundle.EMPTY, false, false);
+        mClearRequest = new ClearCredentialStateRequest(Bundle.EMPTY);
+
+        final Slice slice = new Slice.Builder(Uri.parse("foo://bar"), null).addText("some text",
+                null, List.of(Slice.HINT_TITLE)).build();
+        mRegisterRequest = new RegisterCredentialDescriptionRequest(
+                new CredentialDescription(Credential.TYPE_PASSWORD_CREDENTIAL,
+                        "{ \"foo\": \"bar\" }",
+                        List.of(new CredentialEntry(Credential.TYPE_PASSWORD_CREDENTIAL, slice))));
+        mUnregisterRequest = new UnregisterCredentialDescriptionRequest(
+                new CredentialDescription(Credential.TYPE_PASSWORD_CREDENTIAL,
+                        "{ \"foo\": \"bar\" }",
+                        List.of(new CredentialEntry(Credential.TYPE_PASSWORD_CREDENTIAL, slice))));
+
+        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        mCredentialManager = new CredentialManager(context, mMockCredentialManagerService);
+        mExecutor = Runnable::run;
+        mPackageName = context.getOpPackageName();
+    }
+
+    @Test
+    public void testGetCredential_nullRequest() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.getCredential(null, mMockActivity, null, mExecutor,
+                        result -> {
+                        }));
+    }
+
+    @Test
+    public void testGetCredential_nullActivity() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.getCredential(mGetRequest, null, null, mExecutor,
+                        result -> {
+                        }));
+    }
+
+    @Test
+    public void testGetCredential_nullExecutor() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.getCredential(mGetRequest, mMockActivity, null, null,
+                        result -> {
+                        }));
+    }
+
+    @Test
+    public void testGetCredential_nullCallback() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.getCredential(mGetRequest, mMockActivity, null, null,
+                        null));
+    }
+
+    @Test
+    public void testGetCredential_noCredential() throws RemoteException {
+        ArgumentCaptor<IGetCredentialCallback> callbackCaptor = ArgumentCaptor.forClass(
+                IGetCredentialCallback.class);
+        ArgumentCaptor<GetCredentialException> errorCaptor = ArgumentCaptor.forClass(
+                GetCredentialException.class);
+
+        OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback = mock(
+                OutcomeReceiver.class);
+
+        when(mMockCredentialManagerService.executeGetCredential(any(), callbackCaptor.capture(),
+                any())).thenReturn(mock(ICancellationSignal.class));
+        mCredentialManager.getCredential(mGetRequest, mMockActivity, null, mExecutor, callback);
+        verify(mMockCredentialManagerService).executeGetCredential(any(), any(), eq(mPackageName));
+
+        callbackCaptor.getValue().onError(GetCredentialException.TYPE_NO_CREDENTIAL,
+                "no credential found");
+        verify(callback).onError(errorCaptor.capture());
+
+        assertThat(errorCaptor.getValue().getType()).isEqualTo(
+                GetCredentialException.TYPE_NO_CREDENTIAL);
+    }
+
+    @Test
+    public void testGetCredential_alreadyCancelled() throws RemoteException {
+        final CancellationSignal cancellation = new CancellationSignal();
+        cancellation.cancel();
+
+        mCredentialManager.getCredential(mGetRequest, mMockActivity, cancellation, mExecutor,
+                result -> {
+                });
+
+        verify(mMockCredentialManagerService, never()).executeGetCredential(any(), any(), any());
+    }
+
+    @Test
+    public void testGetCredential_cancel() throws RemoteException {
+        final ICancellationSignal serviceSignal = mock(ICancellationSignal.class);
+        final CancellationSignal cancellation = new CancellationSignal();
+
+        OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback = mock(
+                OutcomeReceiver.class);
+
+        when(mMockCredentialManagerService.executeGetCredential(any(), any(), any())).thenReturn(
+                serviceSignal);
+
+        mCredentialManager.getCredential(mGetRequest, mMockActivity, cancellation, mExecutor,
+                callback);
+
+        verify(mMockCredentialManagerService).executeGetCredential(any(), any(), eq(mPackageName));
+
+        cancellation.cancel();
+        verify(serviceSignal).cancel();
+    }
+
+    @Test
+    public void testGetCredential_success() throws RemoteException {
+        final Credential cred = new Credential(Credential.TYPE_PASSWORD_CREDENTIAL, Bundle.EMPTY);
+
+        ArgumentCaptor<IGetCredentialCallback> callbackCaptor = ArgumentCaptor.forClass(
+                IGetCredentialCallback.class);
+        ArgumentCaptor<GetCredentialResponse> responseCaptor = ArgumentCaptor.forClass(
+                GetCredentialResponse.class);
+
+        OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback = mock(
+                OutcomeReceiver.class);
+
+        when(mMockCredentialManagerService.executeGetCredential(any(), callbackCaptor.capture(),
+                any())).thenReturn(mock(ICancellationSignal.class));
+        mCredentialManager.getCredential(mGetRequest, mMockActivity, null, mExecutor, callback);
+        verify(mMockCredentialManagerService).executeGetCredential(any(), any(), eq(mPackageName));
+
+        callbackCaptor.getValue().onResponse(new GetCredentialResponse(cred));
+        verify(callback).onResult(responseCaptor.capture());
+
+        assertThat(responseCaptor.getValue().getCredential().getType()).isEqualTo(cred.getType());
+    }
+
+    @Test
+    public void testCreateCredential_nullRequest() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.createCredential(null, mMockActivity, null, mExecutor,
+                        result -> {
+                        }));
+    }
+
+    @Test
+    public void testCreateCredential_nullActivity() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.createCredential(mCreateRequest, null, null, mExecutor,
+                        result -> {
+                        }));
+    }
+
+    @Test
+    public void testCreateCredential_nullExecutor() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.createCredential(mCreateRequest, mMockActivity, null, null,
+                        result -> {
+                        }));
+    }
+
+    @Test
+    public void testCreateCredential_nullCallback() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.createCredential(mCreateRequest, mMockActivity, null,
+                        mExecutor, null));
+    }
+
+    @Test
+    public void testCreateCredential_alreadyCancelled() throws RemoteException {
+        final CancellationSignal cancellation = new CancellationSignal();
+        cancellation.cancel();
+
+        mCredentialManager.createCredential(mCreateRequest, mMockActivity, cancellation, mExecutor,
+                result -> {
+                });
+
+        verify(mMockCredentialManagerService, never()).executeCreateCredential(any(), any(), any());
+    }
+
+    @Test
+    public void testCreateCredential_cancel() throws RemoteException {
+        final ICancellationSignal serviceSignal = mock(ICancellationSignal.class);
+        final CancellationSignal cancellation = new CancellationSignal();
+
+        OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback = mock(
+                OutcomeReceiver.class);
+
+        when(mMockCredentialManagerService.executeCreateCredential(any(), any(), any())).thenReturn(
+                serviceSignal);
+
+        mCredentialManager.createCredential(mCreateRequest, mMockActivity, cancellation, mExecutor,
+                callback);
+
+        verify(mMockCredentialManagerService).executeCreateCredential(any(), any(),
+                eq(mPackageName));
+
+        cancellation.cancel();
+        verify(serviceSignal).cancel();
+    }
+
+    @Test
+    public void testCreateCredential_failed() throws RemoteException {
+        ArgumentCaptor<ICreateCredentialCallback> callbackCaptor = ArgumentCaptor.forClass(
+                ICreateCredentialCallback.class);
+        ArgumentCaptor<CreateCredentialException> errorCaptor = ArgumentCaptor.forClass(
+                CreateCredentialException.class);
+
+        OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback = mock(
+                OutcomeReceiver.class);
+
+        when(mMockCredentialManagerService.executeCreateCredential(any(), callbackCaptor.capture(),
+                any())).thenReturn(mock(ICancellationSignal.class));
+        mCredentialManager.createCredential(mCreateRequest, mMockActivity, null, mExecutor,
+                callback);
+        verify(mMockCredentialManagerService).executeCreateCredential(any(), any(),
+                eq(mPackageName));
+
+        callbackCaptor.getValue().onError(CreateCredentialException.TYPE_UNKNOWN, "unknown error");
+        verify(callback).onError(errorCaptor.capture());
+
+        assertThat(errorCaptor.getValue().getType()).isEqualTo(
+                CreateCredentialException.TYPE_UNKNOWN);
+    }
+
+    @Test
+    public void testCreateCredential_success() throws RemoteException {
+        final Bundle responseData = new Bundle();
+        responseData.putString("foo", "bar");
+
+        ArgumentCaptor<ICreateCredentialCallback> callbackCaptor = ArgumentCaptor.forClass(
+                ICreateCredentialCallback.class);
+        ArgumentCaptor<CreateCredentialResponse> responseCaptor = ArgumentCaptor.forClass(
+                CreateCredentialResponse.class);
+
+        OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback = mock(
+                OutcomeReceiver.class);
+
+        when(mMockCredentialManagerService.executeCreateCredential(any(), callbackCaptor.capture(),
+                any())).thenReturn(mock(ICancellationSignal.class));
+        mCredentialManager.createCredential(mCreateRequest, mMockActivity, null, mExecutor,
+                callback);
+        verify(mMockCredentialManagerService).executeCreateCredential(any(), any(),
+                eq(mPackageName));
+
+        callbackCaptor.getValue().onResponse(new CreateCredentialResponse(responseData));
+        verify(callback).onResult(responseCaptor.capture());
+
+        assertBundleEquals(responseCaptor.getValue().getData(), responseData);
+    }
+
+    @Test
+    public void testClearCredentialState_nullRequest() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.clearCredentialState(null, null, mExecutor, result -> {
+                }));
+    }
+
+    @Test
+    public void testClearCredentialState_nullExecutor() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.clearCredentialState(mClearRequest, null, null, result -> {
+                }));
+    }
+
+    @Test
+    public void testClearCredentialState_nullCallback() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.clearCredentialState(mClearRequest, null, mExecutor,
+                        null));
+    }
+
+    @Test
+    public void testClearCredential_alreadyCancelled() throws RemoteException {
+        final CancellationSignal cancellation = new CancellationSignal();
+        cancellation.cancel();
+
+        mCredentialManager.clearCredentialState(mClearRequest, cancellation, mExecutor, result -> {
+        });
+
+        verify(mMockCredentialManagerService, never()).clearCredentialState(any(), any(), any());
+    }
+
+    @Test
+    public void testClearCredential_cancel() throws RemoteException {
+        final ICancellationSignal serviceSignal = mock(ICancellationSignal.class);
+        final CancellationSignal cancellation = new CancellationSignal();
+
+        OutcomeReceiver<Void, ClearCredentialStateException> callback = mock(OutcomeReceiver.class);
+
+        when(mMockCredentialManagerService.clearCredentialState(any(), any(), any())).thenReturn(
+                serviceSignal);
+
+        mCredentialManager.clearCredentialState(mClearRequest, cancellation, mExecutor, callback);
+
+        verify(mMockCredentialManagerService).clearCredentialState(any(), any(), eq(mPackageName));
+
+        cancellation.cancel();
+        verify(serviceSignal).cancel();
+    }
+
+    @Test
+    public void testClearCredential_failed() throws RemoteException {
+        ArgumentCaptor<IClearCredentialStateCallback> callbackCaptor = ArgumentCaptor.forClass(
+                IClearCredentialStateCallback.class);
+        ArgumentCaptor<ClearCredentialStateException> errorCaptor = ArgumentCaptor.forClass(
+                ClearCredentialStateException.class);
+
+        OutcomeReceiver<Void, ClearCredentialStateException> callback = mock(OutcomeReceiver.class);
+
+        when(mMockCredentialManagerService.clearCredentialState(any(), callbackCaptor.capture(),
+                any())).thenReturn(mock(ICancellationSignal.class));
+        mCredentialManager.clearCredentialState(mClearRequest, null, mExecutor, callback);
+        verify(mMockCredentialManagerService).clearCredentialState(any(), any(), eq(mPackageName));
+
+        callbackCaptor.getValue().onError(ClearCredentialStateException.TYPE_UNKNOWN,
+                "unknown error");
+        verify(callback).onError(errorCaptor.capture());
+
+        assertThat(errorCaptor.getValue().getType()).isEqualTo(
+                ClearCredentialStateException.TYPE_UNKNOWN);
+    }
+
+    @Test
+    public void testClearCredential_success() throws RemoteException {
+        ArgumentCaptor<IClearCredentialStateCallback> callbackCaptor = ArgumentCaptor.forClass(
+                IClearCredentialStateCallback.class);
+
+        OutcomeReceiver<Void, ClearCredentialStateException> callback = mock(OutcomeReceiver.class);
+
+        when(mMockCredentialManagerService.clearCredentialState(any(), callbackCaptor.capture(),
+                any())).thenReturn(mock(ICancellationSignal.class));
+        mCredentialManager.clearCredentialState(mClearRequest, null, mExecutor, callback);
+        verify(mMockCredentialManagerService).clearCredentialState(any(), any(), eq(mPackageName));
+
+        callbackCaptor.getValue().onSuccess();
+        verify(callback).onResult(any());
+    }
+
+    @Test
+    public void testListEnabledProviders_nullExecutor() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.listEnabledProviders(null, null, result -> {
+                }));
+
+    }
+
+    @Test
+    public void testListEnabledProviders_nullCallback() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.listEnabledProviders(null, mExecutor, null));
+
+    }
+
+    @Test
+    public void testListEnabledProviders_alreadyCancelled() throws RemoteException {
+        final CancellationSignal cancellation = new CancellationSignal();
+        cancellation.cancel();
+
+        mCredentialManager.listEnabledProviders(cancellation, mExecutor, result -> {
+        });
+
+        verify(mMockCredentialManagerService, never()).listEnabledProviders(any());
+    }
+
+    @Test
+    public void testListEnabledProviders_cancel() throws RemoteException {
+        final ICancellationSignal serviceSignal = mock(ICancellationSignal.class);
+        final CancellationSignal cancellation = new CancellationSignal();
+
+        OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException> callback =
+                mock(OutcomeReceiver.class);
+
+        when(mMockCredentialManagerService.listEnabledProviders(any())).thenReturn(serviceSignal);
+
+        mCredentialManager.listEnabledProviders(cancellation, mExecutor, callback);
+
+        verify(mMockCredentialManagerService).listEnabledProviders(any());
+
+        cancellation.cancel();
+        verify(serviceSignal).cancel();
+    }
+
+    @Test
+    public void testListEnabledProviders_failed() throws RemoteException {
+        ArgumentCaptor<IListEnabledProvidersCallback> callbackCaptor = ArgumentCaptor.forClass(
+                IListEnabledProvidersCallback.class);
+        ArgumentCaptor<ListEnabledProvidersException> errorCaptor = ArgumentCaptor.forClass(
+                ListEnabledProvidersException.class);
+
+        OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException> callback =
+                mock(OutcomeReceiver.class);
+
+        when(mMockCredentialManagerService.listEnabledProviders(
+                callbackCaptor.capture())).thenReturn(mock(ICancellationSignal.class));
+        mCredentialManager.listEnabledProviders(null, mExecutor, callback);
+        verify(mMockCredentialManagerService).listEnabledProviders(any());
+
+        final String errorType = "type";
+        callbackCaptor.getValue().onError("type", "unknown error");
+        verify(callback).onError(errorCaptor.capture());
+
+        assertThat(errorCaptor.getValue().getType()).isEqualTo(errorType);
+    }
+
+    @Test
+    public void testListEnabledProviders_success() throws RemoteException {
+        ListEnabledProvidersResponse response = ListEnabledProvidersResponse.create(
+                List.of("foo", "bar", "baz"));
+
+        OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException> callback =
+                mock(OutcomeReceiver.class);
+
+        ArgumentCaptor<IListEnabledProvidersCallback> callbackCaptor = ArgumentCaptor.forClass(
+                IListEnabledProvidersCallback.class);
+        ArgumentCaptor<ListEnabledProvidersResponse> responseCaptor = ArgumentCaptor.forClass(
+                ListEnabledProvidersResponse.class);
+
+        when(mMockCredentialManagerService.listEnabledProviders(
+                callbackCaptor.capture())).thenReturn(mock(ICancellationSignal.class));
+        mCredentialManager.listEnabledProviders(null, mExecutor, callback);
+
+        verify(mMockCredentialManagerService).listEnabledProviders(any());
+
+        callbackCaptor.getValue().onResponse(response);
+
+        verify(callback).onResult(responseCaptor.capture());
+        assertThat(responseCaptor.getValue().getProviderComponentNames()).containsExactlyElementsIn(
+                response.getProviderComponentNames());
+    }
+
+    @Test
+    public void testSetEnabledProviders_nullProviders() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.setEnabledProviders(null, 0, mExecutor, response -> {
+                }));
+
+    }
+
+    @Test
+    public void testSetEnabledProviders_nullExecutor() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.setEnabledProviders(List.of("foo"), 0, null, response -> {
+                }));
+
+    }
+
+    @Test
+    public void testSetEnabledProviders_nullCallback() {
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.setEnabledProviders(List.of("foo"), 0, mExecutor, null));
+
+    }
+
+    @Test
+    public void testSetEnabledProviders_failed() throws RemoteException {
+        OutcomeReceiver<Void, SetEnabledProvidersException> callback = mock(OutcomeReceiver.class);
+
+        ArgumentCaptor<ISetEnabledProvidersCallback> callbackCaptor = ArgumentCaptor.forClass(
+                ISetEnabledProvidersCallback.class);
+        ArgumentCaptor<SetEnabledProvidersException> errorCaptor = ArgumentCaptor.forClass(
+                SetEnabledProvidersException.class);
+
+        final List<String> providers = List.of("foo", "bar");
+        final int userId = 0;
+        mCredentialManager.setEnabledProviders(providers, userId, mExecutor, callback);
+        verify(mMockCredentialManagerService).setEnabledProviders(eq(providers), eq(0),
+                callbackCaptor.capture());
+
+        final String errorType = "unknown";
+        final String errorMessage = "Unknown error";
+        callbackCaptor.getValue().onError(errorType, errorMessage);
+        verify(callback).onError(errorCaptor.capture());
+
+        assertThat(errorCaptor.getValue().getType()).isEqualTo(errorType);
+        assertThat(errorCaptor.getValue().getMessage()).isEqualTo(errorMessage);
+    }
+
+    @Test
+    public void testSetEnabledProviders_success() throws RemoteException {
+        OutcomeReceiver<Void, SetEnabledProvidersException> callback = mock(OutcomeReceiver.class);
+
+        ArgumentCaptor<ISetEnabledProvidersCallback> callbackCaptor = ArgumentCaptor.forClass(
+                ISetEnabledProvidersCallback.class);
+
+        final List<String> providers = List.of("foo", "bar");
+        final int userId = 0;
+        mCredentialManager.setEnabledProviders(providers, userId, mExecutor, callback);
+
+        verify(mMockCredentialManagerService).setEnabledProviders(eq(providers), eq(0),
+                callbackCaptor.capture());
+
+        callbackCaptor.getValue().onResponse();
+        verify(callback).onResult(any());
+    }
+
+    @Test
+    public void testRegisterCredentialDescription_nullRequest() {
+        assumeTrue(CredentialManager.isCredentialDescriptionApiEnabled());
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.registerCredentialDescription(null));
+    }
+
+    @Test
+    public void testRegisterCredentialDescription_success() throws RemoteException {
+        assumeTrue(CredentialManager.isCredentialDescriptionApiEnabled());
+
+        mCredentialManager.registerCredentialDescription(mRegisterRequest);
+        verify(mMockCredentialManagerService).registerCredentialDescription(same(mRegisterRequest),
+                eq(mPackageName));
+    }
+
+    @Test
+    public void testUnregisterCredentialDescription_nullRequest() {
+        assumeTrue(CredentialManager.isCredentialDescriptionApiEnabled());
+
+        assertThrows(NullPointerException.class,
+                () -> mCredentialManager.unregisterCredentialDescription(null));
+    }
+
+    @Test
+    public void testUnregisterCredentialDescription_success() throws RemoteException {
+        assumeTrue(CredentialManager.isCredentialDescriptionApiEnabled());
+
+        mCredentialManager.unregisterCredentialDescription(mUnregisterRequest);
+        verify(mMockCredentialManagerService).unregisterCredentialDescription(
+                same(mUnregisterRequest), eq(mPackageName));
+    }
+}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index b1abc2a1..a39dd08 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -2175,23 +2175,26 @@
 
     public static final @NonNull Parcelable.Creator<Bitmap> CREATOR
             = new Parcelable.Creator<Bitmap>() {
-        /**
-         * Rebuilds a bitmap previously stored with writeToParcel().
-         *
-         * @param p    Parcel object to read the bitmap from
-         * @return a new bitmap created from the data in the parcel
-         */
-        public Bitmap createFromParcel(Parcel p) {
-            Bitmap bm = nativeCreateFromParcel(p);
-            if (bm == null) {
-                throw new RuntimeException("Failed to unparcel Bitmap");
-            }
-            return bm;
-        }
-        public Bitmap[] newArray(int size) {
-            return new Bitmap[size];
-        }
-    };
+                /**
+                 * Rebuilds a bitmap previously stored with writeToParcel().
+                 *
+                 * @param p    Parcel object to read the bitmap from
+                 * @return a new bitmap created from the data in the parcel
+                 */
+                public Bitmap createFromParcel(Parcel p) {
+                    Bitmap bm = nativeCreateFromParcel(p);
+                    if (bm == null) {
+                        throw new RuntimeException("Failed to unparcel Bitmap");
+                    }
+                    if (p.readBoolean()) {
+                        bm.setGainmap(p.readTypedObject(Gainmap.CREATOR));
+                    }
+                    return bm;
+                }
+                public Bitmap[] newArray(int size) {
+                    return new Bitmap[size];
+                }
+            };
 
     /**
      * No special parcel contents.
@@ -2215,6 +2218,12 @@
         if (!nativeWriteToParcel(mNativePtr, mDensity, p)) {
             throw new RuntimeException("native writeToParcel failed");
         }
+        if (hasGainmap()) {
+            p.writeBoolean(true);
+            p.writeTypedObject(mGainmap, flags);
+        } else {
+            p.writeBoolean(false);
+        }
     }
 
     /**
diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java
index 53f23c0..470a06c 100644
--- a/graphics/java/android/graphics/Gainmap.java
+++ b/graphics/java/android/graphics/Gainmap.java
@@ -18,6 +18,8 @@
 
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -76,7 +78,7 @@
  *
  * In the above math, log() is a natural logarithm and exp() is natural exponentiation.
  */
-public final class Gainmap {
+public final class Gainmap implements Parcelable {
 
     // Use a Holder to allow static initialization of Gainmap in the boot image.
     private static class NoImagePreloadHolder {
@@ -284,6 +286,50 @@
         return nGetDisplayRatioSdr(mNativePtr);
     }
 
+    /**
+     * No special parcel contents.
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Write the gainmap to the parcel.
+     *
+     * @param dest Parcel object to write the gainmap data into
+     * @param flags Additional flags about how the object should be written.
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        if (mNativePtr == 0) {
+            throw new IllegalStateException("Cannot be written to a parcel");
+        }
+        dest.writeTypedObject(mGainmapContents, flags);
+        // write gainmapinfo into parcel
+        nWriteGainmapToParcel(mNativePtr, dest);
+    }
+
+    public static final @NonNull Parcelable.Creator<Gainmap> CREATOR =
+            new Parcelable.Creator<Gainmap>() {
+            /**
+             * Rebuilds a gainmap previously stored with writeToParcel().
+             *
+             * @param in Parcel object to read the gainmap from
+             * @return a new gainmap created from the data in the parcel
+             */
+            public Gainmap createFromParcel(Parcel in) {
+                Gainmap gm = new Gainmap(in.readTypedObject(Bitmap.CREATOR));
+                // read gainmapinfo from parcel
+                nReadGainmapFromParcel(gm.mNativePtr, in);
+                return gm;
+            }
+
+            public Gainmap[] newArray(int size) {
+                return new Gainmap[size];
+            }
+        };
+
     private static native long nGetFinalizer();
     private static native long nCreateEmpty();
 
@@ -309,4 +355,6 @@
 
     private static native void nSetDisplayRatioSdr(long ptr, float min);
     private static native float nGetDisplayRatioSdr(long ptr);
+    private static native void nWriteGainmapToParcel(long ptr, Parcel dest);
+    private static native void nReadGainmapFromParcel(long ptr, Parcel src);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index bc81710..c9c0e40 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -41,6 +41,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.window.DisplayAreaInfo;
@@ -363,10 +364,10 @@
         }
         ProtoLog.d(WM_SHELL_DESKTOP_MODE, "handle shell transition request: %s", request);
 
-        WindowContainerTransaction wct = mTransitions.dispatchRequest(transition, request, this);
-        if (wct == null) {
-            wct = new WindowContainerTransaction();
-        }
+        Pair<Transitions.TransitionHandler, WindowContainerTransaction> subHandler =
+                mTransitions.dispatchRequest(transition, request, this);
+        WindowContainerTransaction wct = subHandler != null
+                ? subHandler.second : new WindowContainerTransaction();
         bringDesktopAppsToFront(wct);
         wct.reorder(request.getTriggerTask().token, true /* onTop */);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index a2d7bc4..c2da705 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.transition;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -28,11 +29,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.IBinder;
+import android.util.Pair;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.pip.PipTransitionController;
@@ -61,6 +64,9 @@
         /** Both the display and split-state (enter/exit) is changing */
         static final int TYPE_DISPLAY_AND_SPLIT_CHANGE = 2;
 
+        /** Pip was entered while handling an intent with its own remoteTransition. */
+        static final int TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE = 3;
+
         /** The default animation for this mixed transition. */
         static final int ANIM_TYPE_DEFAULT = 0;
 
@@ -85,6 +91,23 @@
             mType = type;
             mTransition = transition;
         }
+
+        void joinFinishArgs(WindowContainerTransaction wct,
+                WindowContainerTransactionCallback wctCB) {
+            if (wctCB != null) {
+                // Technically can probably support 1, but don't want to encourage CB usage since
+                // it creates instabliity, so just throw.
+                throw new IllegalArgumentException("Can't mix transitions that require finish"
+                        + " sync callback");
+            }
+            if (wct != null) {
+                if (mFinishWCT == null) {
+                    mFinishWCT = wct;
+                } else {
+                    mFinishWCT.merge(wct, true /* transfer */);
+                }
+            }
+        }
     }
 
     private final ArrayList<MixedTransition> mActiveTransitions = new ArrayList<>();
@@ -125,6 +148,25 @@
             mPipHandler.augmentRequest(transition, request, out);
             mSplitHandler.addEnterOrExitIfNeeded(request, out);
             return out;
+        } else if (request.getRemoteTransition() != null
+                && Transitions.isOpeningType(request.getType())
+                && (request.getTriggerTask() == null
+                || (request.getTriggerTask().topActivityType != ACTIVITY_TYPE_HOME
+                        && request.getTriggerTask().topActivityType != ACTIVITY_TYPE_RECENTS))) {
+            // Only select transitions with an intent-provided remote-animation because that will
+            // usually grab priority and often won't handle PiP. If there isn't an intent-provided
+            // remote, then the transition will be dispatched normally and the PipHandler will
+            // pick it up.
+            Pair<Transitions.TransitionHandler, WindowContainerTransaction> handler =
+                    mPlayer.dispatchRequest(transition, request, this);
+            if (handler == null) {
+                return null;
+            }
+            final MixedTransition mixed = new MixedTransition(
+                    MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE, transition);
+            mixed.mLeftoversHandler = handler.first;
+            mActiveTransitions.add(mixed);
+            return handler.second;
         }
         return null;
     }
@@ -169,6 +211,9 @@
                     finishCallback);
         } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
             return false;
+        } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
+            return animateOpenIntentWithRemoteAndPip(mixed, info, startTransaction,
+                    finishTransaction, finishCallback);
         } else {
             mActiveTransitions.remove(mixed);
             throw new IllegalStateException("Starting mixed animation without a known mixed type? "
@@ -176,6 +221,59 @@
         }
     }
 
+    private boolean animateOpenIntentWithRemoteAndPip(@NonNull MixedTransition mixed,
+            @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        TransitionInfo.Change pipChange = null;
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            TransitionInfo.Change change = info.getChanges().get(i);
+            if (mPipHandler.isEnteringPip(change, info.getType())) {
+                if (pipChange != null) {
+                    throw new IllegalStateException("More than 1 pip-entering changes in one"
+                            + " transition? " + info);
+                }
+                pipChange = change;
+                info.getChanges().remove(i);
+            }
+        }
+        if (pipChange == null) {
+            if (mixed.mLeftoversHandler != null) {
+                return mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info,
+                        startTransaction, finishTransaction, finishCallback);
+            }
+            return false;
+        }
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate"
+                        + " animation because remote-animation likely doesn't support it");
+        mixed.mFinishCallback = finishCallback;
+        Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
+            --mixed.mInFlightSubAnimations;
+            mixed.joinFinishArgs(wct, wctCB);
+            if (mixed.mInFlightSubAnimations > 0) return;
+            mActiveTransitions.remove(mixed);
+            mixed.mFinishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB);
+        };
+        // Split the transition into 2 parts: the pip part and the rest.
+        mixed.mInFlightSubAnimations = 2;
+        // make a new startTransaction because pip's startEnterAnimation "consumes" it so
+        // we need a separate one to send over to launcher.
+        SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
+
+        mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB);
+
+        // Dispatch the rest of the transition normally.
+        if (mixed.mLeftoversHandler != null
+                && mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info,
+                    startTransaction, finishTransaction, finishCB)) {
+            return true;
+        }
+        mixed.mLeftoversHandler = mPlayer.dispatchTransition(mixed.mTransition, info,
+                startTransaction, finishTransaction, finishCB, this);
+        return true;
+    }
+
     private boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed,
             @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
@@ -211,12 +309,13 @@
         mixed.mFinishCallback = finishCallback;
         Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
             --mixed.mInFlightSubAnimations;
+            mixed.joinFinishArgs(wct, wctCB);
             if (mixed.mInFlightSubAnimations > 0) return;
             mActiveTransitions.remove(mixed);
             if (isGoingHome) {
                 mSplitHandler.onTransitionAnimationComplete();
             }
-            mixed.mFinishCallback.onTransitionFinished(wct, wctCB);
+            mixed.mFinishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB);
         };
         if (isGoingHome) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed "
@@ -317,17 +416,7 @@
 
         Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
             --mixed.mInFlightSubAnimations;
-            if (wctCB != null) {
-                throw new IllegalArgumentException("Can't mix transitions that require finish"
-                        + " sync callback");
-            }
-            if (wct != null) {
-                if (mixed.mFinishWCT == null) {
-                    mixed.mFinishWCT = wct;
-                } else {
-                    mixed.mFinishWCT.merge(wct, true /* transfer */);
-                }
-            }
+            mixed.joinFinishArgs(wct, wctCB);
             if (mixed.mInFlightSubAnimations > 0) return;
             mActiveTransitions.remove(mixed);
             mixed.mFinishCallback.onTransitionFinished(mixed.mFinishWCT, null /* wctCB */);
@@ -342,8 +431,7 @@
         // Note: at this point, startT has probably already been applied, so we are basically
         // giving splitHandler an empty startT. This is currently OK because display-change will
         // grab a screenshot and paste it on top anyways.
-        mSplitHandler.startPendingAnimation(
-                transition, everythingElse, startT, finishT, finishCB);
+        mSplitHandler.startPendingAnimation(transition, everythingElse, startT, finishT, finishCB);
         return true;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 75d1939..0826fe2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -47,6 +47,7 @@
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.util.Log;
+import android.util.Pair;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.window.ITransitionPlayer;
@@ -627,13 +628,14 @@
      * Gives every handler (in order) a chance to handle request until one consumes the transition.
      * @return the WindowContainerTransaction given by the handler which consumed the transition.
      */
-    public WindowContainerTransaction dispatchRequest(@NonNull IBinder transition,
-            @NonNull TransitionRequestInfo request, @Nullable TransitionHandler skip) {
+    public Pair<TransitionHandler, WindowContainerTransaction> dispatchRequest(
+            @NonNull IBinder transition, @NonNull TransitionRequestInfo request,
+            @Nullable TransitionHandler skip) {
         for (int i = mHandlers.size() - 1; i >= 0; --i) {
             if (mHandlers.get(i) == skip) continue;
             WindowContainerTransaction wct = mHandlers.get(i).handleRequest(transition, request);
             if (wct != null) {
-                return wct;
+                return new Pair<>(mHandlers.get(i), wct);
             }
         }
         return null;
@@ -698,9 +700,9 @@
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                 "Transition animation finished (abort=%b), notifying core %s", abort, transition);
         if (active.mStartT != null) {
-            // Applied by now, so close immediately. Do not set to null yet, though, since nullness
-            // is used later to disambiguate malformed transitions.
-            active.mStartT.close();
+            // Applied by now, so clear immediately to remove any references. Do not set to null
+            // yet, though, since nullness is used later to disambiguate malformed transitions.
+            active.mStartT.clear();
         }
         // Merge all relevant transactions together
         SurfaceControl.Transaction fullFinish = active.mFinishT;
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 44e4a31..de5f2f4 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
@@ -250,25 +250,30 @@
 
         @Override
         public boolean onTouch(View v, MotionEvent e) {
-            boolean isDrag = false;
             final int id = v.getId();
             if (id != R.id.caption_handle && id != R.id.desktop_mode_caption) {
                 return false;
             }
-            if (id == R.id.caption_handle) {
-                isDrag = mDragDetector.onMotionEvent(e);
+            switch (e.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    mDragDetector.onMotionEvent(e);
+                    final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+                    if (taskInfo.isFocused) {
+                        return mDragDetector.isDragEvent();
+                    }
+                    final WindowContainerTransaction wct = new WindowContainerTransaction();
+                    wct.reorder(mTaskToken, true /* onTop */);
+                    mSyncQueue.queue(wct);
+                    return false;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    boolean res = mDragDetector.isDragEvent();
+                    mDragDetector.onMotionEvent(e);
+                    return res;
+                default:
+                    mDragDetector.onMotionEvent(e);
+                    return mDragDetector.isDragEvent();
             }
-            if (e.getAction() != MotionEvent.ACTION_DOWN) {
-                return isDrag;
-            }
-            final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
-            if (taskInfo.isFocused) {
-                return isDrag;
-            }
-            final WindowContainerTransaction wct = new WindowContainerTransaction();
-            wct.reorder(mTaskToken, true /* onTop */);
-            mSyncQueue.queue(wct);
-            return true;
         }
 
         /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index 4fac843..cf1850b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -94,6 +94,10 @@
         mTouchSlop = touchSlop;
     }
 
+    boolean isDragEvent() {
+        return mIsDragEvent;
+    }
+
     private void resetState() {
         mIsDragEvent = false;
         mInputDownPoint.set(0, 0);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 03d89cc..d63b3e7 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -364,6 +364,7 @@
         "jni/PathMeasure.cpp",
         "jni/Picture.cpp",
         "jni/Region.cpp",
+        "jni/ScopedParcel.cpp",
         "jni/Shader.cpp",
         "jni/RenderEffect.cpp",
         "jni/Typeface.cpp",
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index e71a2a5..3f9c4bd 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -10,6 +10,7 @@
 #include "Gainmap.h"
 #include "GraphicsJNI.h"
 #include "HardwareBufferHelpers.h"
+#include "ScopedParcel.h"
 #include "SkBitmap.h"
 #include "SkBlendMode.h"
 #include "SkCanvas.h"
@@ -27,12 +28,7 @@
 
 #ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread
 #include <android-base/unique_fd.h>
-#include <android/binder_parcel.h>
-#include <android/binder_parcel_jni.h>
-#include <android/binder_parcel_platform.h>
-#include <cutils/ashmem.h>
 #include <renderthread/RenderProxy.h>
-#include <sys/mman.h>
 #endif
 
 #include <inttypes.h>
@@ -616,91 +612,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 // TODO: Move somewhere else
-#ifdef __ANDROID__ // Layoutlib does not support parcel
-
-class ScopedParcel {
-public:
-    explicit ScopedParcel(JNIEnv* env, jobject parcel) {
-        mParcel = AParcel_fromJavaParcel(env, parcel);
-    }
-
-    ~ScopedParcel() { AParcel_delete(mParcel); }
-
-    int32_t readInt32() {
-        int32_t temp = 0;
-        // TODO: This behavior-matches what android::Parcel does
-        // but this should probably be better
-        if (AParcel_readInt32(mParcel, &temp) != STATUS_OK) {
-            temp = 0;
-        }
-        return temp;
-    }
-
-    uint32_t readUint32() {
-        uint32_t temp = 0;
-        // TODO: This behavior-matches what android::Parcel does
-        // but this should probably be better
-        if (AParcel_readUint32(mParcel, &temp) != STATUS_OK) {
-            temp = 0;
-        }
-        return temp;
-    }
-
-    void writeInt32(int32_t value) { AParcel_writeInt32(mParcel, value); }
-
-    void writeUint32(uint32_t value) { AParcel_writeUint32(mParcel, value); }
-
-    bool allowFds() const { return AParcel_getAllowFds(mParcel); }
-
-    std::optional<sk_sp<SkData>> readData() {
-        struct Data {
-            void* ptr = nullptr;
-            size_t size = 0;
-        } data;
-        auto error = AParcel_readByteArray(mParcel, &data,
-                                           [](void* arrayData, int32_t length,
-                                              int8_t** outBuffer) -> bool {
-                                               Data* data = reinterpret_cast<Data*>(arrayData);
-                                               if (length > 0) {
-                                                   data->ptr = sk_malloc_canfail(length);
-                                                   if (!data->ptr) {
-                                                       return false;
-                                                   }
-                                                   *outBuffer =
-                                                           reinterpret_cast<int8_t*>(data->ptr);
-                                                   data->size = length;
-                                               }
-                                               return true;
-                                           });
-        if (error != STATUS_OK || data.size <= 0) {
-            sk_free(data.ptr);
-            return std::nullopt;
-        } else {
-            return SkData::MakeFromMalloc(data.ptr, data.size);
-        }
-    }
-
-    void writeData(const std::optional<sk_sp<SkData>>& optData) {
-        if (optData) {
-            const auto& data = *optData;
-            AParcel_writeByteArray(mParcel, reinterpret_cast<const int8_t*>(data->data()),
-                                   data->size());
-        } else {
-            AParcel_writeByteArray(mParcel, nullptr, -1);
-        }
-    }
-
-    AParcel* get() { return mParcel; }
-
-private:
-    AParcel* mParcel;
-};
-
-enum class BlobType : int32_t {
-    IN_PLACE,
-    ASHMEM,
-};
-
+#ifdef __ANDROID__  // Layoutlib does not support parcel
 #define ON_ERROR_RETURN(X) \
     if ((error = (X)) != STATUS_OK) return error
 
diff --git a/libs/hwui/jni/Gainmap.cpp b/libs/hwui/jni/Gainmap.cpp
index 9cd3fb0..0f8a85d 100644
--- a/libs/hwui/jni/Gainmap.cpp
+++ b/libs/hwui/jni/Gainmap.cpp
@@ -16,8 +16,13 @@
 
 #include <Gainmap.h>
 
+#ifdef __ANDROID__
+#include <binder/Parcel.h>
+#endif
+
 #include "Bitmap.h"
 #include "GraphicsJNI.h"
+#include "ScopedParcel.h"
 #include "graphics_jni_helpers.h"
 
 namespace android {
@@ -154,6 +159,81 @@
     return fromJava(gainmapPtr)->info.fDisplayRatioSdr;
 }
 
+// ----------------------------------------------------------------------------
+// Serialization
+// ----------------------------------------------------------------------------
+
+static void Gainmap_writeToParcel(JNIEnv* env, jobject, jlong nativeObject, jobject parcel) {
+#ifdef __ANDROID__  // Layoutlib does not support parcel
+    if (parcel == NULL) {
+        ALOGD("write null parcel\n");
+        return;
+    }
+    ScopedParcel p(env, parcel);
+    SkGainmapInfo info = fromJava(nativeObject)->info;
+    // write gainmap to parcel
+    // ratio min
+    p.writeFloat(info.fGainmapRatioMin.fR);
+    p.writeFloat(info.fGainmapRatioMin.fG);
+    p.writeFloat(info.fGainmapRatioMin.fB);
+    // ratio max
+    p.writeFloat(info.fGainmapRatioMax.fR);
+    p.writeFloat(info.fGainmapRatioMax.fG);
+    p.writeFloat(info.fGainmapRatioMax.fB);
+    // gamma
+    p.writeFloat(info.fGainmapGamma.fR);
+    p.writeFloat(info.fGainmapGamma.fG);
+    p.writeFloat(info.fGainmapGamma.fB);
+    // epsilonsdr
+    p.writeFloat(info.fEpsilonSdr.fR);
+    p.writeFloat(info.fEpsilonSdr.fG);
+    p.writeFloat(info.fEpsilonSdr.fB);
+    // epsilonhdr
+    p.writeFloat(info.fEpsilonHdr.fR);
+    p.writeFloat(info.fEpsilonHdr.fG);
+    p.writeFloat(info.fEpsilonHdr.fB);
+    // display ratio sdr
+    p.writeFloat(info.fDisplayRatioSdr);
+    // display ratio hdr
+    p.writeFloat(info.fDisplayRatioHdr);
+    // base image type
+    p.writeInt32(static_cast<int32_t>(info.fBaseImageType));
+    // type
+    p.writeInt32(static_cast<int32_t>(info.fType));
+#else
+    doThrowRE(env, "Cannot use parcels outside of Android!");
+#endif
+}
+
+static void Gainmap_readFromParcel(JNIEnv* env, jobject, jlong nativeObject, jobject parcel) {
+#ifdef __ANDROID__  // Layoutlib does not support parcel
+    if (parcel == NULL) {
+        jniThrowNullPointerException(env, "parcel cannot be null");
+        return;
+    }
+    ScopedParcel p(env, parcel);
+
+    SkGainmapInfo info;
+    info.fGainmapRatioMin = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
+    info.fGainmapRatioMax = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
+    info.fGainmapGamma = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
+    info.fEpsilonSdr = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
+    info.fEpsilonHdr = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
+    info.fDisplayRatioSdr = p.readFloat();
+    info.fDisplayRatioHdr = p.readFloat();
+    info.fBaseImageType = static_cast<SkGainmapInfo::BaseImageType>(p.readInt32());
+    info.fType = static_cast<SkGainmapInfo::Type>(p.readInt32());
+
+    fromJava(nativeObject)->info = info;
+#else
+    jniThrowRuntimeException(env, "Cannot use parcels outside of Android");
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
 static const JNINativeMethod gGainmapMethods[] = {
         {"nGetFinalizer", "()J", (void*)Gainmap_getNativeFinalizer},
         {"nCreateEmpty", "()J", (void*)Gainmap_createEmpty},
@@ -172,6 +252,8 @@
         {"nGetDisplayRatioHdr", "(J)F", (void*)Gainmap_getDisplayRatioHdr},
         {"nSetDisplayRatioSdr", "(JF)V", (void*)Gainmap_setDisplayRatioSdr},
         {"nGetDisplayRatioSdr", "(J)F", (void*)Gainmap_getDisplayRatioSdr},
+        {"nWriteGainmapToParcel", "(JLandroid/os/Parcel;)V", (void*)Gainmap_writeToParcel},
+        {"nReadGainmapFromParcel", "(JLandroid/os/Parcel;)V", (void*)Gainmap_readFromParcel},
 };
 
 int register_android_graphics_Gainmap(JNIEnv* env) {
diff --git a/libs/hwui/jni/ScopedParcel.cpp b/libs/hwui/jni/ScopedParcel.cpp
new file mode 100644
index 0000000..b0f5423
--- /dev/null
+++ b/libs/hwui/jni/ScopedParcel.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+#include "ScopedParcel.h"
+
+#ifdef __ANDROID__  // Layoutlib does not support parcel
+
+using namespace android;
+
+int32_t ScopedParcel::readInt32() {
+    int32_t temp = 0;
+    // TODO: This behavior-matches what android::Parcel does
+    // but this should probably be better
+    if (AParcel_readInt32(mParcel, &temp) != STATUS_OK) {
+        temp = 0;
+    }
+    return temp;
+}
+
+uint32_t ScopedParcel::readUint32() {
+    uint32_t temp = 0;
+    // TODO: This behavior-matches what android::Parcel does
+    // but this should probably be better
+    if (AParcel_readUint32(mParcel, &temp) != STATUS_OK) {
+        temp = 0;
+    }
+    return temp;
+}
+
+float ScopedParcel::readFloat() {
+    float temp = 0.;
+    if (AParcel_readFloat(mParcel, &temp) != STATUS_OK) {
+        temp = 0.;
+    }
+    return temp;
+}
+
+std::optional<sk_sp<SkData>> ScopedParcel::readData() {
+    struct Data {
+        void* ptr = nullptr;
+        size_t size = 0;
+    } data;
+    auto error = AParcel_readByteArray(
+            mParcel, &data, [](void* arrayData, int32_t length, int8_t** outBuffer) -> bool {
+                Data* data = reinterpret_cast<Data*>(arrayData);
+                if (length > 0) {
+                    data->ptr = sk_malloc_canfail(length);
+                    if (!data->ptr) {
+                        return false;
+                    }
+                    *outBuffer = reinterpret_cast<int8_t*>(data->ptr);
+                    data->size = length;
+                }
+                return true;
+            });
+    if (error != STATUS_OK || data.size <= 0) {
+        sk_free(data.ptr);
+        return std::nullopt;
+    } else {
+        return SkData::MakeFromMalloc(data.ptr, data.size);
+    }
+}
+
+void ScopedParcel::writeData(const std::optional<sk_sp<SkData>>& optData) {
+    if (optData) {
+        const auto& data = *optData;
+        AParcel_writeByteArray(mParcel, reinterpret_cast<const int8_t*>(data->data()),
+                               data->size());
+    } else {
+        AParcel_writeByteArray(mParcel, nullptr, -1);
+    }
+}
+#endif  // __ANDROID__ // Layoutlib does not support parcel
diff --git a/libs/hwui/jni/ScopedParcel.h b/libs/hwui/jni/ScopedParcel.h
new file mode 100644
index 0000000..fd8d6a2
--- /dev/null
+++ b/libs/hwui/jni/ScopedParcel.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+#include "SkData.h"
+
+#ifdef __ANDROID__  // Layoutlib does not support parcel
+#include <android-base/unique_fd.h>
+#include <android/binder_parcel.h>
+#include <android/binder_parcel_jni.h>
+#include <android/binder_parcel_platform.h>
+#include <cutils/ashmem.h>
+#include <renderthread/RenderProxy.h>
+
+class ScopedParcel {
+public:
+    explicit ScopedParcel(JNIEnv* env, jobject parcel) {
+        mParcel = AParcel_fromJavaParcel(env, parcel);
+    }
+
+    ~ScopedParcel() { AParcel_delete(mParcel); }
+
+    int32_t readInt32();
+
+    uint32_t readUint32();
+
+    float readFloat();
+
+    void writeInt32(int32_t value) { AParcel_writeInt32(mParcel, value); }
+
+    void writeUint32(uint32_t value) { AParcel_writeUint32(mParcel, value); }
+
+    void writeFloat(float value) { AParcel_writeFloat(mParcel, value); }
+
+    bool allowFds() const { return AParcel_getAllowFds(mParcel); }
+
+    std::optional<sk_sp<SkData>> readData();
+
+    void writeData(const std::optional<sk_sp<SkData>>& optData);
+
+    AParcel* get() { return mParcel; }
+
+private:
+    AParcel* mParcel;
+};
+
+enum class BlobType : int32_t {
+    IN_PLACE,
+    ASHMEM,
+};
+
+#endif  // __ANDROID__ // Layoutlib does not support parcel
\ No newline at end of file
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 7ef0f77..9a6d5d7 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -30,7 +30,6 @@
 #include <camera/Camera.h>
 #include <media/mediarecorder.h>
 #include <media/MediaMetricsItem.h>
-#include <media/MicrophoneInfo.h>
 #include <media/stagefright/PersistentSurface.h>
 #include <utils/threads.h>
 
@@ -774,7 +773,7 @@
     }
 
     jint jStatus = AUDIO_JAVA_SUCCESS;
-    std::vector<media::MicrophoneInfo> activeMicrophones;
+    std::vector<media::MicrophoneInfoFw> activeMicrophones;
     status_t status = mr->getActiveMicrophones(&activeMicrophones);
     if (status != NO_ERROR) {
         ALOGE_IF(status != NO_ERROR, "MediaRecorder::getActiveMicrophones error %d", status);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index dcddde4..210a387 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -131,6 +131,7 @@
         Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
         Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
         Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
+        Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
         Settings.Secure.VR_DISPLAY_MODE,
         Settings.Secure.NOTIFICATION_BADGING,
         Settings.Secure.NOTIFICATION_DISMISS_RTL,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 1c5e6ea..39cd24a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -191,6 +191,8 @@
                 ANY_STRING_VALIDATOR);
         VALIDATORS.put(Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
                 ANY_STRING_VALIDATOR);
+        VALIDATORS.put(Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+                ANY_STRING_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index ed6e619..ab36d58 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -51,9 +51,12 @@
     defaultClockProvider: ClockProvider,
     val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
 ) {
-    // Usually this would be a typealias, but a SAM provides better java interop
-    fun interface ClockChangeListener {
-        fun onClockChanged()
+    interface ClockChangeListener {
+        // Called when the active clock changes
+        fun onCurrentClockChanged() {}
+
+        // Called when the list of available clocks changes
+        fun onAvailableClocksChanged() {}
     }
 
     private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
@@ -92,7 +95,7 @@
         protected set(value) {
             if (field != value) {
                 field = value
-                scope.launch(mainDispatcher) { onClockChanged() }
+                scope.launch(mainDispatcher) { onClockChanged { it.onCurrentClockChanged() } }
             }
         }
 
@@ -164,9 +167,9 @@
         Assert.isNotMainThread()
     }
 
-    private fun onClockChanged() {
+    private fun onClockChanged(func: (ClockChangeListener) -> Unit) {
         assertMainThread()
-        clockChangeListeners.forEach { it.onClockChanged() }
+        clockChangeListeners.forEach(func)
     }
 
     private fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) {
@@ -241,6 +244,7 @@
     }
 
     private fun connectClocks(provider: ClockProvider) {
+        var isAvailableChanged = false
         val currentId = currentClockId
         for (clock in provider.getClocks()) {
             val id = clock.clockId
@@ -251,10 +255,11 @@
                     "Clock Id conflict: $id is registered by both " +
                         "${provider::class.simpleName} and ${current.provider::class.simpleName}"
                 )
-                return
+                continue
             }
 
             availableClocks[id] = ClockInfo(clock, provider)
+            isAvailableChanged = true
             if (DEBUG) {
                 Log.i(TAG, "Added ${clock.clockId}")
             }
@@ -263,24 +268,35 @@
                 if (DEBUG) {
                     Log.i(TAG, "Current clock ($currentId) was connected")
                 }
-                onClockChanged()
+                onClockChanged { it.onCurrentClockChanged() }
             }
         }
+
+        if (isAvailableChanged) {
+            onClockChanged { it.onAvailableClocksChanged() }
+        }
     }
 
     private fun disconnectClocks(provider: ClockProvider) {
+        var isAvailableChanged = false
         val currentId = currentClockId
         for (clock in provider.getClocks()) {
             availableClocks.remove(clock.clockId)
+            isAvailableChanged = true
+
             if (DEBUG) {
                 Log.i(TAG, "Removed ${clock.clockId}")
             }
 
             if (currentId == clock.clockId) {
                 Log.w(TAG, "Current clock ($currentId) was disconnected")
-                onClockChanged()
+                onClockChanged { it.onCurrentClockChanged() }
             }
         }
+
+        if (isAvailableChanged) {
+            onClockChanged { it.onAvailableClocksChanged() }
+        }
     }
 
     fun getClocks(): List<ClockMetadata> {
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 176bb6b..ec860b5 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -451,7 +451,6 @@
 -packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
@@ -736,7 +735,6 @@
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/ShadeExpansionStateManagerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
index 91f3309..26eefa9 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
@@ -252,49 +252,49 @@
             DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 90.0,
                     (s) -> highestSurface(s));
 
-    public static final DynamicColor primaryFixedDarker =
+    public static final DynamicColor primaryFixedDim =
             DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 80.0,
                     (s) -> highestSurface(s));
 
     public static final DynamicColor onPrimaryFixed =
             DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 10.0,
-                    (s) -> primaryFixedDarker);
+                    (s) -> primaryFixedDim);
 
     public static final DynamicColor onPrimaryFixedVariant =
             DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 30.0,
-                    (s) -> primaryFixedDarker);
+                    (s) -> primaryFixedDim);
 
     public static final DynamicColor secondaryFixed =
             DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 90.0,
                     (s) -> highestSurface(s));
 
-    public static final DynamicColor secondaryFixedDarker =
+    public static final DynamicColor secondaryFixedDim =
             DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 80.0,
                     (s) -> highestSurface(s));
 
     public static final DynamicColor onSecondaryFixed =
             DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 10.0,
-                    (s) -> secondaryFixedDarker);
+                    (s) -> secondaryFixedDim);
 
     public static final DynamicColor onSecondaryFixedVariant =
             DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 30.0,
-                    (s) -> secondaryFixedDarker);
+                    (s) -> secondaryFixedDim);
 
     public static final DynamicColor tertiaryFixed =
             DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 90.0,
                     (s) -> highestSurface(s));
 
-    public static final DynamicColor tertiaryFixedDarker =
+    public static final DynamicColor tertiaryFixedDim =
             DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 80.0,
                     (s) -> highestSurface(s));
 
     public static final DynamicColor onTertiaryFixed =
             DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 10.0,
-                    (s) -> tertiaryFixedDarker);
+                    (s) -> tertiaryFixedDim);
 
     public static final DynamicColor onTertiaryFixedVariant =
             DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 30.0,
-                    (s) -> tertiaryFixedDarker);
+                    (s) -> tertiaryFixedDim);
 
     /**
      * These colors were present in Android framework before Android U, and used by MDC controls.
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 12e5f19..558cbd4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -460,7 +460,7 @@
     <integer name="watch_heap_limit">256000</integer>
 
     <!-- SystemUI Plugins that can be loaded on user builds. -->
-    <string-array name="config_pluginWhitelist" translatable="false">
+    <string-array name="config_pluginAllowlist" translatable="false">
         <item>com.android.systemui</item>
     </string-array>
 
@@ -834,7 +834,7 @@
     <string name="config_wallpaperPickerPackage" translatable="false">
         com.android.wallpaper
     </string>
-    
+
     <!-- Whether the floating rotation button should be on the left/right in the device's natural
          orientation -->
     <bool name="floating_rotation_button_position_left">true</bool>
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index ead1a10..c4f1ce8 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -29,8 +29,9 @@
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
-import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
 import android.util.Log
 import com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser
 import com.android.systemui.Dumpable
@@ -60,9 +61,27 @@
      * Indicates the origin for an active unlock request.
      */
     enum class ActiveUnlockRequestOrigin {
+        /**
+         * Trigger ActiveUnlock on wake ups that'd trigger FaceAuth, see [FaceWakeUpTriggersConfig]
+         */
         WAKE,
+
+        /**
+         * Trigger ActiveUnlock on unlock intents. This includes the bouncer showing or tapping on
+         * a notification. May also include wakeups: [wakeupsConsideredUnlockIntents].
+         */
         UNLOCK_INTENT,
+
+        /**
+         * Trigger ActiveUnlock on biometric failures. This may include soft errors depending on
+         * the other settings. See: [faceErrorsToTriggerBiometricFailOn],
+         * [faceAcquireInfoToTriggerBiometricFailOn].
+         */
         BIOMETRIC_FAIL,
+
+        /**
+         * Trigger ActiveUnlock when the assistant is triggered.
+         */
         ASSISTANT,
     }
 
@@ -85,6 +104,7 @@
     private var faceAcquireInfoToTriggerBiometricFailOn = mutableSetOf<Int>()
     private var onUnlockIntentWhenBiometricEnrolled = mutableSetOf<Int>()
     private var wakeupsConsideredUnlockIntents = mutableSetOf<Int>()
+    private var wakeupsToForceDismissKeyguard = mutableSetOf<Int>()
 
     private val settingsObserver = object : ContentObserver(handler) {
         private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
@@ -97,6 +117,8 @@
                 secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
         private val wakeupsConsideredUnlockIntentsUri =
             secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS)
+        private val wakeupsToForceDismissKeyguardUri =
+            secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD)
 
         fun register() {
             registerUri(
@@ -108,6 +130,7 @@
                         faceAcquireInfoUri,
                         unlockIntentWhenBiometricEnrolledUri,
                         wakeupsConsideredUnlockIntentsUri,
+                        wakeupsToForceDismissKeyguardUri,
                     )
             )
 
@@ -182,6 +205,15 @@
                     wakeupsConsideredUnlockIntents,
                     setOf(WAKE_REASON_UNFOLD_DEVICE))
             }
+
+            if (selfChange || uris.contains(wakeupsToForceDismissKeyguardUri)) {
+                processStringArray(
+                    secureSettings.getStringForUser(
+                        ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+                        getCurrentUser()),
+                    wakeupsToForceDismissKeyguard,
+                    setOf(WAKE_REASON_UNFOLD_DEVICE))
+            }
         }
 
         /**
@@ -249,6 +281,14 @@
     }
 
     /**
+     * Whether the PowerManager wake reason should force dismiss the keyguard if active
+     * unlock is successful.
+     */
+    fun shouldWakeupForceDismissKeyguard(pmWakeReason: Int): Boolean {
+        return wakeupsToForceDismissKeyguard.contains(pmWakeReason)
+    }
+
+    /**
      * Whether to trigger active unlock based on where the request is coming from and
      * the current settings.
      */
@@ -321,6 +361,9 @@
         pw.println("   activeUnlockWakeupsConsideredUnlockIntents=${
             wakeupsConsideredUnlockIntents.map { PowerManager.wakeReasonToString(it) }
         }")
+        pw.println("   activeUnlockFromWakeupsToAlwaysDismissKeyguard=${
+            wakeupsToForceDismissKeyguard.map { PowerManager.wakeReasonToString(it) }
+        }")
 
         pw.println("Current state:")
         keyguardUpdateMonitor?.let {
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt b/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
index a0c43fb..788a66d 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
@@ -29,7 +29,7 @@
 import java.util.stream.Collectors
 import javax.inject.Inject
 
-/** Determines which device wake-ups should trigger face authentication. */
+/** Determines which device wake-ups should trigger passive authentication. */
 @SysUISingleton
 class FaceWakeUpTriggersConfig
 @Inject
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index f1b90e3..5ec59ab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -151,8 +151,13 @@
         mLogBuffer = logBuffer;
         mView.setLogBuffer(mLogBuffer);
 
-        mClockChangedListener = () -> {
-            setClock(mClockRegistry.createCurrentClock());
+        mClockChangedListener = new ClockRegistry.ClockChangeListener() {
+            @Override
+            public void onCurrentClockChanged() {
+                setClock(mClockRegistry.createCurrentClock());
+            }
+            @Override
+            public void onAvailableClocksChanged() { }
         };
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
index 998dc09..57130ed 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
@@ -27,6 +27,7 @@
     override var userId: Int = 0,
     override var listening: Boolean = false,
     // keepSorted
+    var alternateBouncerShowing: Boolean = false,
     var biometricEnabledForUser: Boolean = false,
     var bouncerIsOrWillShow: Boolean = false,
     var canSkipBouncer: Boolean = false,
@@ -57,6 +58,7 @@
             userId.toString(),
             listening.toString(),
             // keep sorted
+            alternateBouncerShowing.toString(),
             biometricEnabledForUser.toString(),
             bouncerIsOrWillShow.toString(),
             canSkipBouncer.toString(),
@@ -96,6 +98,7 @@
                 userId = model.userId
                 listening = model.listening
                 // keep sorted
+                alternateBouncerShowing = model.alternateBouncerShowing
                 biometricEnabledForUser = model.biometricEnabledForUser
                 bouncerIsOrWillShow = model.bouncerIsOrWillShow
                 canSkipBouncer = model.canSkipBouncer
@@ -141,6 +144,7 @@
                 "userId",
                 "listening",
                 // keep sorted
+                "alternateBouncerShowing",
                 "biometricAllowedForUser",
                 "bouncerIsOrWillShow",
                 "canSkipBouncer",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 5b628f8..438c4d2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1942,11 +1942,23 @@
             FACE_AUTH_UPDATED_STARTED_WAKING_UP.setExtraInfo(pmWakeReason);
             updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
                     FACE_AUTH_UPDATED_STARTED_WAKING_UP);
-            requestActiveUnlock(
+
+            final ActiveUnlockConfig.ActiveUnlockRequestOrigin requestOrigin =
                     mActiveUnlockConfig.isWakeupConsideredUnlockIntent(pmWakeReason)
                             ? ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
-                            : ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE,
-                    "wakingUp - " + PowerManager.wakeReasonToString(pmWakeReason));
+                            : ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE;
+            final String reason = "wakingUp - " + PowerManager.wakeReasonToString(pmWakeReason);
+            if (mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(pmWakeReason)) {
+                requestActiveUnlockDismissKeyguard(
+                        requestOrigin,
+                        reason
+                );
+            } else {
+                requestActiveUnlock(
+                        requestOrigin,
+                        reason
+                );
+            }
         } else {
             mLogger.logSkipUpdateFaceListeningOnWakeup(pmWakeReason);
         }
@@ -2606,6 +2618,7 @@
         }
     }
 
+
     /**
      * Attempts to trigger active unlock from trust agent.
      * Only dismisses the keyguard under certain conditions.
@@ -2648,6 +2661,7 @@
                     ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
                     "alternateBouncer");
         }
+        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
     }
 
     private boolean shouldTriggerActiveUnlock() {
@@ -2732,7 +2746,7 @@
                         || shouldListenForFingerprintAssistant
                         || (mKeyguardOccluded && mIsDreaming)
                         || (mKeyguardOccluded && userDoesNotHaveTrust
-                            && (mOccludingAppRequestingFp || isUdfps));
+                            && (mOccludingAppRequestingFp || isUdfps || mAlternateBouncerShowing));
 
         // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -2773,6 +2787,7 @@
                     System.currentTimeMillis(),
                     user,
                     shouldListen,
+                    mAlternateBouncerShowing,
                     biometricEnabledForUser,
                     mPrimaryBouncerIsOrWillBeShowing,
                     userCanSkipBouncer,
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 413a3ca..bfd99d3 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -468,6 +468,17 @@
         }
     }
 
+    /**
+     * @return whether the userUnlockedWithBiometric state changed
+     */
+    private boolean updateUserUnlockedWithBiometric() {
+        final boolean wasUserUnlockedWithBiometric = mUserUnlockedWithBiometric;
+        mUserUnlockedWithBiometric =
+                mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
+                        KeyguardUpdateMonitor.getCurrentUser());
+        return wasUserUnlockedWithBiometric != mUserUnlockedWithBiometric;
+    }
+
     private StatusBarStateController.StateListener mStatusBarStateListener =
             new StatusBarStateController.StateListener() {
                 @Override
@@ -505,11 +516,7 @@
 
                 @Override
                 public void onBiometricsCleared() {
-                    final boolean wasUserUnlockedWithBiometric = mUserUnlockedWithBiometric;
-                    mUserUnlockedWithBiometric =
-                            mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
-                                    KeyguardUpdateMonitor.getCurrentUser());
-                    if (wasUserUnlockedWithBiometric != mUserUnlockedWithBiometric) {
+                    if (updateUserUnlockedWithBiometric()) {
                         updateVisibility();
                     }
                 }
@@ -518,10 +525,8 @@
                 public void onBiometricRunningStateChanged(boolean running,
                         BiometricSourceType biometricSourceType) {
                     final boolean wasRunningFps = mRunningFPS;
-                    final boolean wasUserUnlockedWithBiometric = mUserUnlockedWithBiometric;
-                    mUserUnlockedWithBiometric =
-                            mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
-                                    KeyguardUpdateMonitor.getCurrentUser());
+                    final boolean userUnlockedWithBiometricChanged =
+                            updateUserUnlockedWithBiometric();
 
                     if (biometricSourceType == FINGERPRINT) {
                         mRunningFPS = running;
@@ -539,8 +544,7 @@
                         }
                     }
 
-                    if (wasUserUnlockedWithBiometric != mUserUnlockedWithBiometric
-                            || wasRunningFps != mRunningFPS) {
+                    if (userUnlockedWithBiometricChanged || wasRunningFps != mRunningFPS) {
                         updateVisibility();
                     }
                 }
@@ -551,6 +555,7 @@
         @Override
         public void onUnlockedChanged() {
             mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
+            updateUserUnlockedWithBiometric();
             updateKeyguardShowing();
             updateVisibility();
         }
@@ -568,9 +573,7 @@
 
             updateKeyguardShowing();
             if (mIsKeyguardShowing) {
-                mUserUnlockedWithBiometric =
-                    mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
-                        KeyguardUpdateMonitor.getCurrentUser());
+                updateUserUnlockedWithBiometric();
             }
             updateVisibility();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
index 621b99d..6721c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
 import com.android.systemui.keyguard.domain.backup.KeyguardQuickAffordanceBackupHelper
 import com.android.systemui.people.widget.PeopleBackupHelper
+import com.android.systemui.settings.UserFileManagerImpl
 
 /**
  * Helper for backing up elements in SystemUI
@@ -58,17 +59,8 @@
 
     override fun onCreate(userHandle: UserHandle, operationType: Int) {
         super.onCreate()
-        // The map in mapOf is guaranteed to be order preserving
-        val controlsMap = mapOf(CONTROLS to getPPControlsFile(this))
-        NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also {
-            addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it)
-        }
 
-        // Conversations widgets backup only works for system user, because widgets' information is
-        // stored in system user's SharedPreferences files and we can't open those from other users.
-        if (!userHandle.isSystem) {
-            return
-        }
+        addControlsHelper(userHandle.identifier)
 
         val keys = PeopleBackupHelper.getFilesToBackup()
         addHelper(
@@ -95,6 +87,18 @@
         sendBroadcastAsUser(intent, UserHandle.SYSTEM, PERMISSION_SELF)
     }
 
+    private fun addControlsHelper(userId: Int) {
+        val file = UserFileManagerImpl.createFile(
+            userId = userId,
+            fileName = CONTROLS,
+        )
+        // The map in mapOf is guaranteed to be order preserving
+        val controlsMap = mapOf(file.getPath() to getPPControlsFile(this, userId))
+        NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also {
+            addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it)
+        }
+    }
+
     /**
      * Helper class for restoring files ONLY if they are not present.
      *
@@ -136,17 +140,21 @@
     }
 }
 
-private fun getPPControlsFile(context: Context): () -> Unit {
+private fun getPPControlsFile(context: Context, userId: Int): () -> Unit {
     return {
-        val filesDir = context.filesDir
-        val file = Environment.buildPath(filesDir, BackupHelper.CONTROLS)
+        val file = UserFileManagerImpl.createFile(
+            userId = userId,
+            fileName = BackupHelper.CONTROLS,
+        )
         if (file.exists()) {
-            val dest =
-                Environment.buildPath(filesDir, AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME)
+            val dest = UserFileManagerImpl.createFile(
+                userId = userId,
+                fileName = AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME,
+            )
             file.copyTo(dest)
             val jobScheduler = context.getSystemService(JobScheduler::class.java)
             jobScheduler?.schedule(
-                AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context)
+                AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context, userId)
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt
index 0a6335e..b3c18fb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt
@@ -21,8 +21,10 @@
 import android.app.job.JobService
 import android.content.ComponentName
 import android.content.Context
+import android.os.PersistableBundle
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.backup.BackupHelper
+import com.android.systemui.settings.UserFileManagerImpl
 import java.io.File
 import java.util.concurrent.Executor
 import java.util.concurrent.TimeUnit
@@ -33,14 +35,14 @@
  * This file is a copy of the `controls_favorites.xml` file restored from a back up. It is used to
  * keep track of controls that were restored but its corresponding app has not been installed yet.
  */
-class AuxiliaryPersistenceWrapper @VisibleForTesting internal constructor(
-    wrapper: ControlsFavoritePersistenceWrapper
-) {
+class AuxiliaryPersistenceWrapper
+@VisibleForTesting
+internal constructor(wrapper: ControlsFavoritePersistenceWrapper) {
 
     constructor(
         file: File,
         executor: Executor
-    ): this(ControlsFavoritePersistenceWrapper(file, executor))
+    ) : this(ControlsFavoritePersistenceWrapper(file, executor))
 
     companion object {
         const val AUXILIARY_FILE_NAME = "aux_controls_favorites.xml"
@@ -48,9 +50,7 @@
 
     private var persistenceWrapper: ControlsFavoritePersistenceWrapper = wrapper
 
-    /**
-     * Access the current list of favorites as tracked by the auxiliary file
-     */
+    /** Access the current list of favorites as tracked by the auxiliary file */
     var favorites: List<StructureInfo> = emptyList()
         private set
 
@@ -73,18 +73,19 @@
      * exist, it will be initialized to an empty list.
      */
     fun initialize() {
-        favorites = if (persistenceWrapper.fileExists) {
-            persistenceWrapper.readFavorites()
-        } else {
-            emptyList()
-        }
+        favorites =
+            if (persistenceWrapper.fileExists) {
+                persistenceWrapper.readFavorites()
+            } else {
+                emptyList()
+            }
     }
 
     /**
      * Gets the list of favorite controls as persisted in the auxiliary file for a given component.
      *
-     * When the favorites for that application are returned, they will be removed from the
-     * auxiliary file immediately, so they won't be retrieved again.
+     * When the favorites for that application are returned, they will be removed from the auxiliary
+     * file immediately, so they won't be retrieved again.
      * @param componentName the name of the service that provided the controls
      * @return a list of structures with favorites
      */
@@ -103,20 +104,20 @@
         }
     }
 
-    /**
-     * [JobService] to delete the auxiliary file after a week.
-     */
+    /** [JobService] to delete the auxiliary file after a week. */
     class DeletionJobService : JobService() {
         companion object {
-            @VisibleForTesting
-            internal val DELETE_FILE_JOB_ID = 1000
+            @VisibleForTesting internal val DELETE_FILE_JOB_ID = 1000
+            @VisibleForTesting internal val USER = "USER"
             private val WEEK_IN_MILLIS = TimeUnit.DAYS.toMillis(7)
-            fun getJobForContext(context: Context): JobInfo {
+            fun getJobForContext(context: Context, targetUserId: Int): JobInfo {
                 val jobId = DELETE_FILE_JOB_ID + context.userId
                 val componentName = ComponentName(context, DeletionJobService::class.java)
+                val bundle = PersistableBundle().also { it.putInt(USER, targetUserId) }
                 return JobInfo.Builder(jobId, componentName)
                     .setMinimumLatency(WEEK_IN_MILLIS)
                     .setPersisted(true)
+                    .setExtras(bundle)
                     .build()
             }
         }
@@ -127,8 +128,14 @@
         }
 
         override fun onStartJob(params: JobParameters): Boolean {
+            val userId = params.getExtras()?.getInt(USER, 0) ?: 0
             synchronized(BackupHelper.controlsDataLock) {
-                baseContext.deleteFile(AUXILIARY_FILE_NAME)
+                val file =
+                    UserFileManagerImpl.createFile(
+                        userId = userId,
+                        fileName = AUXILIARY_FILE_NAME,
+                    )
+                baseContext.deleteFile(file.getPath())
             }
             return false
         }
@@ -137,4 +144,4 @@
             return true // reschedule and try again if the job was stopped without completing
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index ad5ad13..9921b1f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.dreams.AssistantAttentionMonitor
 import com.android.systemui.dreams.DreamMonitor
 import com.android.systemui.globalactions.GlobalActionsComponent
+import com.android.systemui.keyboard.PhysicalKeyboardCoreStartable
 import com.android.systemui.keyboard.KeyboardUI
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable
@@ -294,6 +295,11 @@
     @ClassKey(StylusUsiPowerStartable::class)
     abstract fun bindStylusUsiPowerStartable(sysui: StylusUsiPowerStartable): CoreStartable
 
+    @Binds
+    @IntoMap
+    @ClassKey(PhysicalKeyboardCoreStartable::class)
+    abstract fun bindKeyboardCoreStartable(listener: PhysicalKeyboardCoreStartable): CoreStartable
+
     /** Inject into MuteQuickAffordanceCoreStartable*/
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index a1050ec..f6bb85f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -48,6 +48,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.FlagsModule;
 import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.keyboard.KeyboardModule;
 import com.android.systemui.keyguard.data.BouncerViewModule;
 import com.android.systemui.log.dagger.LogModule;
 import com.android.systemui.mediaprojection.appselector.MediaProjectionModule;
@@ -151,6 +152,7 @@
             SystemPropertiesFlagsModule.class,
             FooterActionsModule.class,
             GarbageMonitorModule.class,
+            KeyboardModule.class,
             LogModule.class,
             MediaProjectionModule.class,
             MotionToolModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
new file mode 100644
index 0000000..e9b8908
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
@@ -0,0 +1,22 @@
+/*
+ *  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.keyboard
+
+import dagger.Module
+
+@Module abstract class KeyboardModule
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
new file mode 100644
index 0000000..b0f9c4e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
@@ -0,0 +1,38 @@
+/*
+ *  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.keyboard
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+
+/** A [CoreStartable] that launches components interested in physical keyboard interaction. */
+@SysUISingleton
+class PhysicalKeyboardCoreStartable
+@Inject
+constructor(
+    private val featureFlags: FeatureFlags,
+) : CoreStartable {
+    override fun start() {
+        if (featureFlags.isEnabled(Flags.KEYBOARD_BACKLIGHT_INDICATOR)) {
+            // TODO(b/268645743) start listening for keyboard backlight brightness
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 2fb53f1..0ca9115 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -928,7 +928,7 @@
         }
 
         // The smartspace is not visible if the bouncer is showing, so don't shared element it.
-        if (keyguardStateController.isBouncerShowing) {
+        if (keyguardStateController.isPrimaryBouncerShowing) {
             return false
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c5ea241..54fc5b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1150,12 +1150,12 @@
     private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
             new KeyguardStateController.Callback() {
         @Override
-        public void onBouncerShowingChanged() {
+        public void onPrimaryBouncerShowingChanged() {
             synchronized (KeyguardViewMediator.this) {
-                if (mKeyguardStateController.isBouncerShowing()) {
+                if (mKeyguardStateController.isPrimaryBouncerShowing()) {
                     mPendingPinLock = false;
                 }
-                adjustStatusBarLocked(mKeyguardStateController.isBouncerShowing(), false);
+                adjustStatusBarLocked(mKeyguardStateController.isPrimaryBouncerShowing(), false);
             }
         }
     };
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 091acad..4331fe6 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
@@ -68,8 +68,8 @@
     val resourceUpdateRequests: StateFlow<Boolean>
     val bouncerPromptReason: Int
     val bouncerErrorMessage: CharSequence?
-    val isAlternateBouncerVisible: StateFlow<Boolean>
-    val isAlternateBouncerUIAvailable: StateFlow<Boolean>
+    val alternateBouncerVisible: StateFlow<Boolean>
+    val alternateBouncerUIAvailable: StateFlow<Boolean>
     var lastAlternateBouncerVisibleTime: Long
 
     fun setPrimaryScrimmed(isScrimmed: Boolean)
@@ -159,12 +159,12 @@
         get() = viewMediatorCallback.consumeCustomMessage()
 
     /** Values associated with the AlternateBouncer */
-    private val _isAlternateBouncerVisible = MutableStateFlow(false)
-    override val isAlternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
+    private val _alternateBouncerVisible = MutableStateFlow(false)
+    override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow()
     override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE
-    private val _isAlternateBouncerUIAvailable = MutableStateFlow(false)
-    override val isAlternateBouncerUIAvailable: StateFlow<Boolean> =
-        _isAlternateBouncerUIAvailable.asStateFlow()
+    private val _alternateBouncerUIAvailable = MutableStateFlow(false)
+    override val alternateBouncerUIAvailable: StateFlow<Boolean> =
+        _alternateBouncerUIAvailable.asStateFlow()
 
     init {
         setUpLogging()
@@ -179,16 +179,16 @@
     }
 
     override fun setAlternateVisible(isVisible: Boolean) {
-        if (isVisible && !_isAlternateBouncerVisible.value) {
+        if (isVisible && !_alternateBouncerVisible.value) {
             lastAlternateBouncerVisibleTime = clock.uptimeMillis()
         } else if (!isVisible) {
             lastAlternateBouncerVisibleTime = NOT_VISIBLE
         }
-        _isAlternateBouncerVisible.value = isVisible
+        _alternateBouncerVisible.value = isVisible
     }
 
     override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
-        _isAlternateBouncerUIAvailable.value = isAvailable
+        _alternateBouncerUIAvailable.value = isAvailable
     }
 
     override fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?) {
@@ -290,7 +290,7 @@
         resourceUpdateRequests
             .logDiffsForTable(buffer, "", "ResourceUpdateRequests", false)
             .launchIn(applicationScope)
-        isAlternateBouncerUIAvailable
+        alternateBouncerUIAvailable
             .logDiffsForTable(buffer, "", "IsAlternateBouncerUIAvailable", false)
             .launchIn(applicationScope)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index db95562..a3b3d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -86,9 +86,6 @@
     /** Observable for the signal that keyguard is about to go away. */
     val isKeyguardGoingAway: Flow<Boolean>
 
-    /** Observable for whether the bouncer is showing. */
-    val isBouncerShowing: Flow<Boolean>
-
     /** Is the always-on display available to be used? */
     val isAodAvailable: Flow<Boolean>
 
@@ -304,29 +301,6 @@
         awaitClose { keyguardStateController.removeCallback(callback) }
     }
 
-    override val isBouncerShowing: Flow<Boolean> = conflatedCallbackFlow {
-        val callback =
-            object : KeyguardStateController.Callback {
-                override fun onBouncerShowingChanged() {
-                    trySendWithFailureLogging(
-                        keyguardStateController.isBouncerShowing,
-                        TAG,
-                        "updated isBouncerShowing"
-                    )
-                }
-            }
-
-        keyguardStateController.addCallback(callback)
-        // Adding the callback does not send an initial update.
-        trySendWithFailureLogging(
-            keyguardStateController.isBouncerShowing,
-            TAG,
-            "initial isBouncerShowing"
-        )
-
-        awaitClose { keyguardStateController.removeCallback(callback) }
-    }
-
     override val isDozing: Flow<Boolean> =
         conflatedCallbackFlow {
                 val callback =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt
index 0e865ce..fa6efa5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt
@@ -29,16 +29,9 @@
 ) :
     SharedPreferencesBackupHelper(
         context,
-        if (UserFileManagerImpl.isPrimaryUser(userId)) {
-            KeyguardQuickAffordanceSelectionManager.FILE_NAME
-        } else {
-            UserFileManagerImpl.secondaryUserFile(
-                    context = context,
-                    fileName = KeyguardQuickAffordanceSelectionManager.FILE_NAME,
-                    directoryName = UserFileManagerImpl.SHARED_PREFS,
-                    userId = userId,
-                )
-                .also { UserFileManagerImpl.ensureParentDirExists(it) }
-                .toString()
-        }
+        UserFileManagerImpl.createFile(
+                userId = userId,
+                fileName = KeyguardQuickAffordanceSelectionManager.FILE_NAME,
+            )
+            .getPath()
     )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
index 6452e0e..dfe1038 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
@@ -44,7 +44,7 @@
     var legacyAlternateBouncer: LegacyAlternateBouncer? = null
     var legacyAlternateBouncerVisibleTime: Long = NOT_VISIBLE
 
-    val isVisible: Flow<Boolean> = bouncerRepository.isAlternateBouncerVisible
+    val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
 
     /**
      * Sets the correct bouncer states to show the alternate bouncer if it can show.
@@ -86,7 +86,7 @@
 
     fun isVisibleState(): Boolean {
         return if (isModernAlternateBouncerEnabled) {
-            bouncerRepository.isAlternateBouncerVisible.value
+            bouncerRepository.alternateBouncerVisible.value
         } else {
             legacyAlternateBouncer?.isShowingAlternateBouncer ?: false
         }
@@ -98,7 +98,7 @@
 
     fun canShowAlternateBouncerForFingerprint(): Boolean {
         return if (isModernAlternateBouncerEnabled) {
-            bouncerRepository.isAlternateBouncerUIAvailable.value &&
+            bouncerRepository.alternateBouncerUIAvailable.value &&
                 biometricSettingsRepository.isFingerprintEnrolled.value &&
                 biometricSettingsRepository.isStrongBiometricAllowed.value &&
                 biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
new file mode 100644
index 0000000..310f44d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -0,0 +1,158 @@
+/*
+ * 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.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class FromAlternateBouncerTransitionInteractor
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    private val keyguardInteractor: KeyguardInteractor,
+    private val keyguardTransitionRepository: KeyguardTransitionRepository,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) : TransitionInteractor(FromAlternateBouncerTransitionInteractor::class.simpleName!!) {
+
+    override fun start() {
+        listenForAlternateBouncerToGone()
+        listenForAlternateBouncerToLockscreenAodOrDozing()
+        listenForAlternateBouncerToPrimaryBouncer()
+    }
+
+    private fun listenForAlternateBouncerToLockscreenAodOrDozing() {
+        scope.launch {
+            keyguardInteractor.alternateBouncerShowing
+                // Add a slight delay, as alternateBouncer and primaryBouncer showing event changes
+                // will arrive with a small gap in time. This prevents a transition to LOCKSCREEN
+                // happening prematurely.
+                .onEach { delay(50) }
+                .sample(
+                    combine(
+                        keyguardInteractor.primaryBouncerShowing,
+                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
+                        keyguardInteractor.wakefulnessModel,
+                        keyguardInteractor.isAodAvailable,
+                        ::toQuad
+                    ),
+                    ::toQuint
+                )
+                .collect {
+                    (
+                        isAlternateBouncerShowing,
+                        isPrimaryBouncerShowing,
+                        lastStartedTransitionStep,
+                        wakefulnessState,
+                        isAodAvailable
+                    ) ->
+                    if (
+                        !isAlternateBouncerShowing &&
+                            !isPrimaryBouncerShowing &&
+                            lastStartedTransitionStep.to == KeyguardState.ALTERNATE_BOUNCER
+                    ) {
+                        val to =
+                            if (
+                                wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
+                                    wakefulnessState.state == WakefulnessState.ASLEEP
+                            ) {
+                                if (isAodAvailable) {
+                                    KeyguardState.AOD
+                                } else {
+                                    KeyguardState.DOZING
+                                }
+                            } else {
+                                KeyguardState.LOCKSCREEN
+                            }
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                ownerName = name,
+                                from = KeyguardState.ALTERNATE_BOUNCER,
+                                to = to,
+                                animator = getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
+    private fun listenForAlternateBouncerToGone() {
+        scope.launch {
+            keyguardInteractor.isKeyguardGoingAway
+                .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
+                .collect { (isKeyguardGoingAway, keyguardState) ->
+                    if (isKeyguardGoingAway && keyguardState == KeyguardState.ALTERNATE_BOUNCER) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                ownerName = name,
+                                from = KeyguardState.ALTERNATE_BOUNCER,
+                                to = KeyguardState.GONE,
+                                animator = getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
+    private fun listenForAlternateBouncerToPrimaryBouncer() {
+        scope.launch {
+            keyguardInteractor.primaryBouncerShowing
+                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (isPrimaryBouncerShowing, startedKeyguardState) ->
+                    if (
+                        isPrimaryBouncerShowing &&
+                            startedKeyguardState.to == KeyguardState.ALTERNATE_BOUNCER
+                    ) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                ownerName = name,
+                                from = KeyguardState.ALTERNATE_BOUNCER,
+                                to = KeyguardState.PRIMARY_BOUNCER,
+                                animator = getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
+    private fun getAnimator(): ValueAnimator {
+        return ValueAnimator().apply {
+            interpolator = Interpolators.LINEAR
+            duration = TRANSITION_DURATION_MS
+        }
+    }
+
+    companion object {
+        private const val TRANSITION_DURATION_MS = 300L
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 5674e2a..d01f489 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -33,7 +33,6 @@
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
@@ -53,9 +52,10 @@
         listenForLockscreenToOccluded()
         listenForLockscreenToCamera()
         listenForLockscreenToAodOrDozing()
-        listenForLockscreenToBouncer()
+        listenForLockscreenToPrimaryBouncer()
         listenForLockscreenToDreaming()
-        listenForLockscreenToBouncerDragging()
+        listenForLockscreenToPrimaryBouncerDragging()
+        listenForLockscreenToAlternateBouncer()
     }
 
     private fun listenForLockscreenToDreaming() {
@@ -78,9 +78,9 @@
         }
     }
 
-    private fun listenForLockscreenToBouncer() {
+    private fun listenForLockscreenToPrimaryBouncer() {
         scope.launch {
-            keyguardInteractor.isBouncerShowing
+            keyguardInteractor.primaryBouncerShowing
                 .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
                 .collect { pair ->
                     val (isBouncerShowing, lastStartedTransitionStep) = pair
@@ -91,7 +91,30 @@
                             TransitionInfo(
                                 ownerName = name,
                                 from = KeyguardState.LOCKSCREEN,
-                                to = KeyguardState.BOUNCER,
+                                to = KeyguardState.PRIMARY_BOUNCER,
+                                animator = getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
+    private fun listenForLockscreenToAlternateBouncer() {
+        scope.launch {
+            keyguardInteractor.alternateBouncerShowing
+                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { pair ->
+                    val (isAlternateBouncerShowing, lastStartedTransitionStep) = pair
+                    if (
+                        isAlternateBouncerShowing &&
+                            lastStartedTransitionStep.to == KeyguardState.LOCKSCREEN
+                    ) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                ownerName = name,
+                                from = KeyguardState.LOCKSCREEN,
+                                to = KeyguardState.ALTERNATE_BOUNCER,
                                 animator = getAnimator(),
                             )
                         )
@@ -101,7 +124,7 @@
     }
 
     /* Starts transitions when manually dragging up the bouncer from the lockscreen. */
-    private fun listenForLockscreenToBouncerDragging() {
+    private fun listenForLockscreenToPrimaryBouncerDragging() {
         var transitionId: UUID? = null
         scope.launch {
             shadeRepository.shadeModel
@@ -144,7 +167,7 @@
                             keyguardTransitionRepository.startTransition(
                                 TransitionInfo(
                                     ownerName = name,
-                                    from = KeyguardState.BOUNCER,
+                                    from = KeyguardState.PRIMARY_BOUNCER,
                                     to = KeyguardState.LOCKSCREEN,
                                     animator = getAnimator(0.milliseconds)
                                 )
@@ -163,7 +186,7 @@
                                     TransitionInfo(
                                         ownerName = name,
                                         from = KeyguardState.LOCKSCREEN,
-                                        to = KeyguardState.BOUNCER,
+                                        to = KeyguardState.PRIMARY_BOUNCER,
                                         animator = null,
                                     )
                                 )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromBouncerTransitionInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 0e9c447..b59b413 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -24,62 +24,63 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.keyguard.shared.model.WakefulnessState
-import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.util.kotlin.sample
-import java.util.UUID
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
 @SysUISingleton
-class FromBouncerTransitionInteractor
+class FromPrimaryBouncerTransitionInteractor
 @Inject
 constructor(
     @Application private val scope: CoroutineScope,
     private val keyguardInteractor: KeyguardInteractor,
-    private val shadeRepository: ShadeRepository,
     private val keyguardTransitionRepository: KeyguardTransitionRepository,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor
-) : TransitionInteractor(FromBouncerTransitionInteractor::class.simpleName!!) {
-
-    private var transitionId: UUID? = null
+) : TransitionInteractor(FromPrimaryBouncerTransitionInteractor::class.simpleName!!) {
 
     override fun start() {
-        listenForBouncerToGone()
-        listenForBouncerToLockscreenOrAod()
+        listenForPrimaryBouncerToGone()
+        listenForPrimaryBouncerToLockscreenAodOrDozing()
     }
 
-    private fun listenForBouncerToLockscreenOrAod() {
+    private fun listenForPrimaryBouncerToLockscreenAodOrDozing() {
         scope.launch {
-            keyguardInteractor.isBouncerShowing
+            keyguardInteractor.primaryBouncerShowing
                 .sample(
                     combine(
                         keyguardInteractor.wakefulnessModel,
                         keyguardTransitionInteractor.startedKeyguardTransitionStep,
-                        ::Pair
+                        keyguardInteractor.isAodAvailable,
+                        ::toTriple
                     ),
-                    ::toTriple
+                    ::toQuad
                 )
-                .collect { triple ->
-                    val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
+                .collect {
+                    (isBouncerShowing, wakefulnessState, lastStartedTransitionStep, isAodAvailable)
+                    ->
                     if (
-                        !isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
+                        !isBouncerShowing &&
+                            lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
                     ) {
                         val to =
                             if (
                                 wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
                                     wakefulnessState.state == WakefulnessState.ASLEEP
                             ) {
-                                KeyguardState.AOD
+                                if (isAodAvailable) {
+                                    KeyguardState.AOD
+                                } else {
+                                    KeyguardState.DOZING
+                                }
                             } else {
                                 KeyguardState.LOCKSCREEN
                             }
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 ownerName = name,
-                                from = KeyguardState.BOUNCER,
+                                from = KeyguardState.PRIMARY_BOUNCER,
                                 to = to,
                                 animator = getAnimator(),
                             )
@@ -89,17 +90,17 @@
         }
     }
 
-    private fun listenForBouncerToGone() {
+    private fun listenForPrimaryBouncerToGone() {
         scope.launch {
             keyguardInteractor.isKeyguardGoingAway
-                .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+                .sample(keyguardTransitionInteractor.finishedKeyguardState) { a, b -> Pair(a, b) }
                 .collect { pair ->
                     val (isKeyguardGoingAway, keyguardState) = pair
-                    if (isKeyguardGoingAway && keyguardState == KeyguardState.BOUNCER) {
+                    if (isKeyguardGoingAway && keyguardState == KeyguardState.PRIMARY_BOUNCER) {
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 ownerName = name,
-                                from = KeyguardState.BOUNCER,
+                                from = KeyguardState.PRIMARY_BOUNCER,
                                 to = KeyguardState.GONE,
                                 animator = getAnimator(),
                             )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 7e86a5d..d25aff0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
@@ -56,6 +57,7 @@
     private val repository: KeyguardRepository,
     private val commandQueue: CommandQueue,
     featureFlags: FeatureFlags,
+    bouncerRepository: KeyguardBouncerRepository,
 ) {
     /**
      * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
@@ -121,8 +123,10 @@
     val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
     /** Whether the keyguard is going away. */
     val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
-    /** Whether the bouncer is showing or not. */
-    val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing
+    /** Whether the primary bouncer is showing or not. */
+    val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerVisible
+    /** Whether the alternate bouncer is showing or not. */
+    val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
     /** The device wake/sleep state */
     val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
     /** Observable for the [StatusBarState] */
@@ -142,12 +146,12 @@
         if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
             combine(
                     isKeyguardVisible,
-                    repository.isBouncerShowing,
+                    bouncerRepository.primaryBouncerVisible,
                     onCameraLaunchDetected,
-                ) { isKeyguardVisible, isBouncerShowing, cameraLaunchEvent ->
+                ) { isKeyguardVisible, isPrimaryBouncerShowing, cameraLaunchEvent ->
                     when {
                         isKeyguardVisible -> false
-                        isBouncerShowing -> false
+                        isPrimaryBouncerShowing -> false
                         else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index d4e23499..51b0277 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.plugins.log.LogLevel.VERBOSE
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.launch
 
 private val TAG = KeyguardTransitionAuditLogger::class.simpleName!!
@@ -46,8 +45,14 @@
         }
 
         scope.launch {
-            keyguardInteractor.isBouncerShowing.collect {
-                logger.log(TAG, VERBOSE, "Bouncer showing", it)
+            keyguardInteractor.primaryBouncerShowing.collect {
+                logger.log(TAG, VERBOSE, "Primary bouncer showing", it)
+            }
+        }
+
+        scope.launch {
+            keyguardInteractor.alternateBouncerShowing.collect {
+                logger.log(TAG, VERBOSE, "Alternate bouncer showing", it)
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index fbed446..efc1bd0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -37,13 +37,14 @@
             // exhaustive
             val ret =
                 when (it) {
-                    is FromBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
+                    is FromPrimaryBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromAodTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromGoneTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromDreamingTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it")
+                    is FromAlternateBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
                 }
             it.start()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 84bcdf9..1b7da5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -21,13 +21,12 @@
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.BOUNCER
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -63,9 +62,9 @@
     /** LOCKSCREEN->AOD transition information. */
     val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
 
-    /** LOCKSCREEN->BOUNCER transition information. */
-    val lockscreenToBouncerTransition: Flow<TransitionStep> =
-        repository.transition(LOCKSCREEN, BOUNCER)
+    /** LOCKSCREEN->PRIMARY_BOUNCER transition information. */
+    val mLockscreenToPrimaryBouncerTransition: Flow<TransitionStep> =
+        repository.transition(LOCKSCREEN, PRIMARY_BOUNCER)
 
     /** LOCKSCREEN->DREAMING transition information. */
     val lockscreenToDreamingTransition: Flow<TransitionStep> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index a59c407..833eda7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -86,7 +86,8 @@
                 KeyguardState.DOZING -> false
                 KeyguardState.AOD -> false
                 KeyguardState.DREAMING -> true
-                KeyguardState.BOUNCER -> true
+                KeyguardState.ALTERNATE_BOUNCER -> true
+                KeyguardState.PRIMARY_BOUNCER -> true
                 KeyguardState.LOCKSCREEN -> true
                 KeyguardState.GONE -> true
                 KeyguardState.OCCLUDED -> true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index 4579e37..e819da9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -156,7 +156,7 @@
         } else {
             DejankUtils.postAfterTraversal(showRunnable)
         }
-        keyguardStateController.notifyBouncerShowing(true)
+        keyguardStateController.notifyPrimaryBouncerShowing(true)
         primaryBouncerCallbackInteractor.dispatchStartingToShow()
         Trace.endSection()
     }
@@ -173,7 +173,7 @@
         }
 
         falsingCollector.onBouncerHidden()
-        keyguardStateController.notifyBouncerShowing(false /* showing */)
+        keyguardStateController.notifyPrimaryBouncerShowing(false /* showing */)
         cancelShowRunnable()
         repository.setPrimaryShowingSoon(false)
         repository.setPrimaryVisible(false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index 81fa233..d9690b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -32,7 +32,9 @@
 
     @Binds
     @IntoSet
-    abstract fun fromBouncer(impl: FromBouncerTransitionInteractor): TransitionInteractor
+    abstract fun fromPrimaryBouncer(
+        impl: FromPrimaryBouncerTransitionInteractor
+    ): TransitionInteractor
 
     @Binds
     @IntoSet
@@ -53,4 +55,10 @@
     @Binds
     @IntoSet
     abstract fun fromDozing(impl: FromDozingTransitionInteractor): TransitionInteractor
+
+    @Binds
+    @IntoSet
+    abstract fun fromAlternateBouncer(
+        impl: FromAlternateBouncerTransitionInteractor
+    ): TransitionInteractor
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 4d24c14..e3e3527 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -30,7 +30,26 @@
 
     abstract fun start()
 
+    fun <A, B, C> toTriple(a: A, b: B, c: C) = Triple(a, b, c)
+
     fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second)
 
     fun <A, B, C> toTriple(ab: Pair<A, B>, c: C) = Triple(ab.first, ab.second, c)
+
+    fun <A, B, C, D> toQuad(a: A, b: B, c: C, d: D) = Quad(a, b, c, d)
+
+    fun <A, B, C, D> toQuad(a: A, bcd: Triple<B, C, D>) = Quad(a, bcd.first, bcd.second, bcd.third)
+
+    fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) =
+        Quint(a, bcde.first, bcde.second, bcde.third, bcde.fourth)
 }
+
+data class Quad<A, B, C, D>(val first: A, val second: B, val third: C, val fourth: D)
+
+data class Quint<A, B, C, D, E>(
+    val first: A,
+    val second: B,
+    val third: C,
+    val fourth: D,
+    val fifth: E
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index c757986..87b4321 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -42,10 +42,15 @@
      */
     AOD,
     /*
-     * The security screen prompt UI, containing PIN, Password, Pattern, and all FPS
-     * (Fingerprint Sensor) variations, for the user to verify their credentials
+     * The security screen prompt containing UI to prompt the user to use a biometric credential
+     * (ie: fingerprint). When supported, this may show before showing the primary bouncer.
      */
-    BOUNCER,
+    ALTERNATE_BOUNCER,
+    /*
+     * The security screen prompt UI, containing PIN, Password, Pattern for the user to verify their
+     * credentials.
+     */
+    PRIMARY_BOUNCER,
     /*
      * Device is actively displaying keyguard UI and is not in low-power mode. Device may be
      * unlocked if SWIPE security method is used, or if face lockscreen bypass is false.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index d977f91..9aecb5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -171,7 +171,12 @@
     }
 
     private fun setUpClock(parentView: ViewGroup) {
-        val clockChangeListener = ClockRegistry.ClockChangeListener { onClockChanged(parentView) }
+        val clockChangeListener =
+            object : ClockRegistry.ClockChangeListener {
+                override fun onCurrentClockChanged() {
+                    onClockChanged(parentView)
+                }
+            }
         clockRegistry.registerClockChangeListener(clockChangeListener)
         disposables.add(
             DisposableHandle { clockRegistry.unregisterClockChangeListener(clockChangeListener) }
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 c02fd9c..bb52659 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -265,16 +265,6 @@
         return factory.create("MediaCarouselCtlrLog", 20);
     }
 
-    /**
-     * Provides a {@link LogBuffer} for use in the status bar connectivity pipeline
-     */
-    @Provides
-    @SysUISingleton
-    @StatusBarConnectivityLog
-    public static LogBuffer provideStatusBarConnectivityBuffer(LogBufferFactory factory) {
-        return factory.create("SbConnectivity", 64);
-    }
-
     /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java
deleted file mode 100644
index 67cdb72..0000000
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.log.dagger;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import com.android.systemui.plugins.log.LogBuffer;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Qualifier;
-
-/**
- * A {@link LogBuffer} for status bar connectivity events.
- */
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface StatusBarConnectivityLog {
-}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
index 146633d..95f1419 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
@@ -121,6 +121,6 @@
     @Provides
     @Named(PLUGIN_PRIVILEGED)
     static List<String> providesPrivilegedPlugins(Context context) {
-        return Arrays.asList(context.getResources().getStringArray(R.array.config_pluginWhitelist));
+        return Arrays.asList(context.getResources().getStringArray(R.array.config_pluginAllowlist));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
index bfba6df..bb637dc 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
@@ -32,74 +32,62 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.util.concurrency.DelayableExecutor
 import java.io.File
+import java.io.FilenameFilter
 import javax.inject.Inject
 
 /**
- * Implementation for retrieving file paths for file storage of system and secondary users. Files
- * lie in {File Directory}/UserFileManager/{User Id} for secondary user. For system user, we use the
- * conventional {File Directory}
+ * Implementation for retrieving file paths for file storage of system and secondary users. For
+ * non-system users, files will be prepended by a special prefix containing the user id.
  */
 @SysUISingleton
 class UserFileManagerImpl
 @Inject
 constructor(
-    // Context of system process and system user.
     private val context: Context,
     val userManager: UserManager,
     val broadcastDispatcher: BroadcastDispatcher,
     @Background val backgroundExecutor: DelayableExecutor
 ) : UserFileManager, CoreStartable {
     companion object {
-        private const val FILES = "files"
+        private const val PREFIX = "__USER_"
+        private const val TAG = "UserFileManagerImpl"
+        const val ROOT_DIR = "UserFileManager"
+        const val FILES = "files"
         const val SHARED_PREFS = "shared_prefs"
-        @VisibleForTesting internal const val ID = "UserFileManager"
-
-        /** Returns `true` if the given user ID is that for the primary/system user. */
-        fun isPrimaryUser(userId: Int): Boolean {
-            return UserHandle(userId).isSystem
-        }
 
         /**
-         * Returns a [File] pointing to the correct path for a secondary user ID.
-         *
-         * Note that there is no check for the type of user. This should only be called for
-         * secondary users, never for the system user. For that, make sure to call [isPrimaryUser].
-         *
-         * Note also that there is no guarantee that the parent directory structure for the file
-         * exists on disk. For that, call [ensureParentDirExists].
-         *
-         * @param context The context
-         * @param fileName The name of the file
-         * @param directoryName The name of the directory that would contain the file
-         * @param userId The ID of the user to build a file path for
+         * Returns a File object with a relative path, built from the userId for non-system users
          */
-        fun secondaryUserFile(
-            context: Context,
-            fileName: String,
-            directoryName: String,
-            userId: Int,
-        ): File {
-            return Environment.buildPath(
-                context.filesDir,
-                ID,
-                userId.toString(),
-                directoryName,
-                fileName,
-            )
-        }
-
-        /**
-         * Checks to see if parent dir of the file exists. If it does not, we create the parent dirs
-         * recursively.
-         */
-        fun ensureParentDirExists(file: File) {
-            val parent = file.parentFile
-            if (!parent.exists()) {
-                if (!parent.mkdirs()) {
-                    Log.e(ID, "Could not create parent directory for file: ${file.absolutePath}")
-                }
+        fun createFile(fileName: String, userId: Int): File {
+            return if (isSystemUser(userId)) {
+                File(fileName)
+            } else {
+                File(getFilePrefix(userId) + fileName)
             }
         }
+
+        fun createLegacyFile(context: Context, dir: String, fileName: String, userId: Int): File? {
+            return if (isSystemUser(userId)) {
+                null
+            } else {
+                return Environment.buildPath(
+                    context.filesDir,
+                    ROOT_DIR,
+                    userId.toString(),
+                    dir,
+                    fileName
+                )
+            }
+        }
+
+        fun getFilePrefix(userId: Int): String {
+            return PREFIX + userId.toString() + "_"
+        }
+
+        /** Returns `true` if the given user ID is that for the system user. */
+        private fun isSystemUser(userId: Int): Boolean {
+            return UserHandle(userId).isSystem
+        }
     }
 
     private val broadcastReceiver =
@@ -119,64 +107,87 @@
         broadcastDispatcher.registerReceiver(broadcastReceiver, filter, backgroundExecutor)
     }
 
-    /** Return the file based on current user. */
+    /**
+     * Return the file based on current user. Files for all users will exist in [context.filesDir],
+     * but non system user files will be prepended with [getFilePrefix].
+     */
     override fun getFile(fileName: String, userId: Int): File {
-        return if (isPrimaryUser(userId)) {
-            Environment.buildPath(context.filesDir, fileName)
-        } else {
-            val secondaryFile =
-                secondaryUserFile(
-                    context = context,
-                    userId = userId,
-                    directoryName = FILES,
-                    fileName = fileName,
-                )
-            ensureParentDirExists(secondaryFile)
-            secondaryFile
-        }
+        val file = File(context.filesDir, createFile(fileName, userId).path)
+        createLegacyFile(context, FILES, fileName, userId)?.run { migrate(file, this) }
+        return file
     }
 
-    /** Get shared preferences from user. */
+    /**
+     * Get shared preferences from user. Files for all users will exist in the shared_prefs dir, but
+     * non system user files will be prepended with [getFilePrefix].
+     */
     override fun getSharedPreferences(
         fileName: String,
         @Context.PreferencesMode mode: Int,
         userId: Int
     ): SharedPreferences {
-        if (isPrimaryUser(userId)) {
-            return context.getSharedPreferences(fileName, mode)
+        val file = createFile(fileName, userId)
+        createLegacyFile(context, SHARED_PREFS, "$fileName.xml", userId)?.run {
+            val path = Environment.buildPath(context.dataDir, SHARED_PREFS, "${file.path}.xml")
+            migrate(path, this)
         }
-
-        val secondaryUserDir =
-            secondaryUserFile(
-                context = context,
-                fileName = fileName,
-                directoryName = SHARED_PREFS,
-                userId = userId,
-            )
-
-        ensureParentDirExists(secondaryUserDir)
-        return context.getSharedPreferences(secondaryUserDir, mode)
+        return context.getSharedPreferences(file.path, mode)
     }
 
-    /** Remove dirs for deleted users. */
+    /** Remove files for deleted users. */
     @VisibleForTesting
     internal fun clearDeletedUserData() {
         backgroundExecutor.execute {
-            val file = Environment.buildPath(context.filesDir, ID)
-            if (!file.exists()) return@execute
-            val aliveUsers = userManager.aliveUsers.map { it.id.toString() }
-            val dirsToDelete = file.list().filter { !aliveUsers.contains(it) }
+            deleteFiles(context.filesDir)
+            deleteFiles(File(context.dataDir, SHARED_PREFS))
+        }
+    }
 
-            dirsToDelete.forEach { dir ->
+    private fun migrate(dest: File, source: File) {
+        if (source.exists()) {
+            try {
+                val parent = source.getParentFile()
+                source.renameTo(dest)
+
+                deleteParentDirsIfEmpty(parent)
+            } catch (e: Exception) {
+                Log.e(TAG, "Failed to rename and delete ${source.path}", e)
+            }
+        }
+    }
+
+    private fun deleteParentDirsIfEmpty(dir: File?) {
+        if (dir != null && dir.listFiles().size == 0) {
+            val priorParent = dir.parentFile
+            val isRoot = dir.name == ROOT_DIR
+            dir.delete()
+
+            if (!isRoot) {
+                deleteParentDirsIfEmpty(priorParent)
+            }
+        }
+    }
+
+    private fun deleteFiles(parent: File) {
+        val aliveUserFilePrefix = userManager.aliveUsers.map { getFilePrefix(it.id) }
+        val filesToDelete =
+            parent.listFiles(
+                FilenameFilter { _, name ->
+                    name.startsWith(PREFIX) &&
+                        aliveUserFilePrefix.filter { name.startsWith(it) }.isEmpty()
+                }
+            )
+
+        // This can happen in test environments
+        if (filesToDelete == null) {
+            Log.i(TAG, "Empty directory: ${parent.path}")
+        } else {
+            filesToDelete.forEach { file ->
+                Log.i(TAG, "Deleting file: ${file.path}")
                 try {
-                    val dirToDelete =
-                        Environment.buildPath(
-                            file,
-                            dir,
-                        )
-                    dirToDelete.deleteRecursively()
+                    file.delete()
                 } catch (e: Exception) {
-                    Log.e(ID, "Deletion failed.", e)
+                    Log.e(TAG, "Deletion failed.", e)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 51125b1..46bc887 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -870,7 +870,8 @@
         }
 
         for (int i = childCount - 1; i >= 0; i--) {
-            updateChildZValue(i, algorithmState, ambientState, i == topHunIndex);
+            childrenOnTop = updateChildZValue(i, childrenOnTop,
+                    algorithmState, ambientState, i == topHunIndex);
         }
     }
 
@@ -880,11 +881,15 @@
      *
      * @param isTopHun      Whether the child is a top HUN. A top HUN means a HUN that shows on the
      *                      vertically top of screen. Top HUNs should have drop shadows
+     * @param childrenOnTop It is greater than 0 when there's an existing HUN that is elevated
+     * @return childrenOnTop The decimal part represents the fraction of the elevated HUN's height
+     *                      that overlaps with QQS Panel. The integer part represents the count of
+     *                      previous HUNs whose Z positions are greater than 0.
      */
-    protected void updateChildZValue(int i,
-                                     StackScrollAlgorithmState algorithmState,
-                                     AmbientState ambientState,
-                                     boolean isTopHun) {
+    protected float updateChildZValue(int i, float childrenOnTop,
+                                      StackScrollAlgorithmState algorithmState,
+                                      AmbientState ambientState,
+                                      boolean isTopHun) {
         ExpandableView child = algorithmState.visibleChildren.get(i);
         ExpandableViewState childViewState = child.getViewState();
         float baseZ = ambientState.getBaseZHeight();
@@ -898,16 +903,22 @@
             // Handles HUN shadow when Shade is opened, and AmbientState.mScrollY > 0
             // Calculate the HUN's z-value based on its overlapping fraction with QQS Panel.
             // When scrolling down shade to make HUN back to in-position in Notification Panel,
-            // the overlapFraction goes to 0, and the pinned HUN's shadows hides gradually.
-            float overlap = ambientState.getTopPadding()
-                    + ambientState.getStackTranslation() - childViewState.getYTranslation();
-
-            if (childViewState.height > 0) { // To avoid 0/0 problems
-                // To prevent over-shadow
-                float overlapFraction = MathUtils.saturate(overlap / childViewState.height);
-                childViewState.setZTranslation(baseZ
-                        + overlapFraction * mPinnedZTranslationExtra);
+            // The over-lapping fraction goes to 0, and shadows hides gradually.
+            if (childrenOnTop != 0.0f) {
+                // To elevate the later HUN over previous HUN
+                childrenOnTop++;
+            } else {
+                float overlap = ambientState.getTopPadding()
+                        + ambientState.getStackTranslation() - childViewState.getYTranslation();
+                // To prevent over-shadow during HUN entry
+                childrenOnTop += Math.min(
+                        1.0f,
+                        overlap / childViewState.height
+                );
+                MathUtils.saturate(childrenOnTop);
             }
+            childViewState.setZTranslation(baseZ
+                    + childrenOnTop * mPinnedZTranslationExtra);
         } else if (isTopHun) {
             // In case this is a new view that has never been measured before, we don't want to
             // elevate if we are currently expanded more than the notification
@@ -935,14 +946,15 @@
         }
 
         // Handles HUN shadow when shade is closed.
-        // While shade is closed, and during HUN's entry: headerVisibleAmount stays 0, shadow stays.
-        // While shade is closed, and HUN is showing: headerVisibleAmount stays 0, shadow stays.
+        // While HUN is showing and Shade is closed: headerVisibleAmount stays 0, shadow stays.
         // During HUN-to-Shade (eg. dragging down HUN to open Shade): headerVisibleAmount goes
         // gradually from 0 to 1, shadow hides gradually.
         // Header visibility is a deprecated concept, we are using headerVisibleAmount only because
         // this value nicely goes from 0 to 1 during the HUN-to-Shade process.
+
         childViewState.setZTranslation(childViewState.getZTranslation()
                 + (1.0f - child.getHeaderVisibleAmount()) * mPinnedZTranslationExtra);
+        return childrenOnTop;
     }
 
     public void setIsExpanded(boolean isExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
index 4a5342e..5d5d562 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
@@ -18,9 +18,10 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
+import com.android.systemui.statusbar.pipeline.dagger.AirplaneTableLog
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
@@ -46,7 +47,7 @@
 @Inject
 constructor(
     interactor: AirplaneModeInteractor,
-    logger: ConnectivityPipelineLogger,
+    @AirplaneTableLog logger: TableLogBuffer,
     @Application private val scope: CoroutineScope,
 ) : AirplaneModeViewModel {
     override val isAirplaneModeIconVisible: StateFlow<Boolean> =
@@ -56,6 +57,11 @@
                 isAirplaneMode && !isAirplaneIconForceHidden
             }
             .distinctUntilChanged()
-            .logOutputChange(logger, "isAirplaneModeIconVisible")
+            .logDiffsForTable(
+                logger,
+                columnPrefix = "",
+                columnName = "isAirplaneModeIconVisible",
+                initialValue = false,
+            )
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileInputLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileInputLog.kt
new file mode 100644
index 0000000..d1aa79e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileInputLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Logs for inputs into the mobile pipeline. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class MobileInputLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/SharedConnectivityInputLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/SharedConnectivityInputLog.kt
new file mode 100644
index 0000000..5face22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/SharedConnectivityInputLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Logs for connectivity-related inputs that are shared across wifi, mobile, etc. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class SharedConnectivityInputLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 60de1a3..4464751 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -19,8 +19,10 @@
 import android.net.wifi.WifiManager
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -107,6 +109,13 @@
 
         @Provides
         @SysUISingleton
+        @WifiInputLog
+        fun provideWifiInputLogBuffer(factory: LogBufferFactory): LogBuffer {
+            return factory.create("WifiInputLog", 50)
+        }
+
+        @Provides
+        @SysUISingleton
         @WifiTableLog
         fun provideWifiTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
             return factory.create("WifiTableLog", 100)
@@ -121,9 +130,23 @@
 
         @Provides
         @SysUISingleton
+        @SharedConnectivityInputLog
+        fun provideSharedConnectivityTableLogBuffer(factory: LogBufferFactory): LogBuffer {
+            return factory.create("SharedConnectivityInputLog", 30)
+        }
+
+        @Provides
+        @SysUISingleton
         @MobileSummaryLog
         fun provideMobileSummaryLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
             return factory.create("MobileSummaryLog", 100)
         }
+
+        @Provides
+        @SysUISingleton
+        @MobileInputLog
+        fun provideMobileInputLogBuffer(factory: LogBufferFactory): LogBuffer {
+            return factory.create("MobileInputLog", 100)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/WifiInputLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/WifiInputLog.kt
new file mode 100644
index 0000000..6db6944
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/WifiInputLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Wifi logs for inputs into the wifi pipeline. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class WifiInputLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
index 5769f90..bb3b9b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
@@ -31,7 +31,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -57,7 +57,7 @@
     broadcastDispatcher: BroadcastDispatcher,
     private val carrierConfigManager: CarrierConfigManager,
     dumpManager: DumpManager,
-    logger: ConnectivityPipelineLogger,
+    logger: MobileInputLogger,
     @Application scope: CoroutineScope,
 ) : Dumpable {
     private var isListening = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index dcce0ea..96b96f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -47,8 +47,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
@@ -85,7 +85,7 @@
     broadcastDispatcher: BroadcastDispatcher,
     private val mobileMappingsProxy: MobileMappingsProxy,
     bgDispatcher: CoroutineDispatcher,
-    logger: ConnectivityPipelineLogger,
+    logger: MobileInputLogger,
     override val tableLogBuffer: TableLogBuffer,
     scope: CoroutineScope,
 ) : MobileConnectionRepository {
@@ -291,7 +291,7 @@
         private val broadcastDispatcher: BroadcastDispatcher,
         private val context: Context,
         private val telephonyManager: TelephonyManager,
-        private val logger: ConnectivityPipelineLogger,
+        private val logger: MobileInputLogger,
         private val carrierConfigRepository: CarrierConfigRepository,
         private val mobileMappingsProxy: MobileMappingsProxy,
         @Background private val bgDispatcher: CoroutineDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 73ce5e6..b3d5b1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -49,9 +49,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.util.kotlin.pairwise
@@ -84,7 +83,7 @@
     private val connectivityManager: ConnectivityManager,
     private val subscriptionManager: SubscriptionManager,
     private val telephonyManager: TelephonyManager,
-    private val logger: ConnectivityPipelineLogger,
+    private val logger: MobileInputLogger,
     @MobileSummaryLog private val tableLogger: TableLogBuffer,
     mobileMappingsProxy: MobileMappingsProxy,
     broadcastDispatcher: BroadcastDispatcher,
@@ -222,13 +221,13 @@
     private val carrierConfigChangedEvent =
         broadcastDispatcher
             .broadcastFlow(IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED))
-            .logInputChange(logger, "ACTION_CARRIER_CONFIG_CHANGED")
+            .onEach { logger.logActionCarrierConfigChanged() }
 
     override val defaultDataSubRatConfig: StateFlow<Config> =
         merge(defaultDataSubIdChangeEvent, carrierConfigChangedEvent)
             .mapLatest { Config.readConfig(context) }
             .distinctUntilChanged()
-            .logInputChange(logger, "defaultDataSubRatConfig")
+            .onEach { logger.logDefaultDataSubRatConfig(it) }
             .stateIn(
                 scope,
                 SharingStarted.WhileSubscribed(),
@@ -239,13 +238,13 @@
         defaultDataSubRatConfig
             .map { mobileMappingsProxy.mapIconSets(it) }
             .distinctUntilChanged()
-            .logInputChange(logger, "defaultMobileIconMapping")
+            .onEach { logger.logDefaultMobileIconMapping(it) }
 
     override val defaultMobileIconGroup: Flow<MobileIconGroup> =
         defaultDataSubRatConfig
             .map { mobileMappingsProxy.getDefaultIcons(it) }
             .distinctUntilChanged()
-            .logInputChange(logger, "defaultMobileIconGroup")
+            .onEach { logger.logDefaultMobileIconGroup(it) }
 
     override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository {
         if (!isValidSubId(subId)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 5a2e11e..142c372 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
 import com.android.systemui.util.CarrierConfigTracker
@@ -111,7 +110,6 @@
 constructor(
     private val mobileConnectionsRepo: MobileConnectionsRepository,
     private val carrierConfigTracker: CarrierConfigTracker,
-    private val logger: ConnectivityPipelineLogger,
     @MobileSummaryLog private val tableLogger: TableLogBuffer,
     connectivityRepository: ConnectivityRepository,
     userSetupRepo: UserSetupRepository,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt
new file mode 100644
index 0000000..3cbd2b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt
@@ -0,0 +1,202 @@
+/*
+ * 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.statusbar.pipeline.mobile.shared
+
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.telephony.ServiceState
+import android.telephony.SignalStrength
+import android.telephony.TelephonyDisplayInfo
+import com.android.settingslib.SignalIcon
+import com.android.settingslib.mobile.MobileMappings
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.MobileInputLog
+import com.android.systemui.statusbar.pipeline.shared.LoggerHelper
+import javax.inject.Inject
+
+/** Logs for inputs into the mobile pipeline. */
+@SysUISingleton
+class MobileInputLogger
+@Inject
+constructor(
+    @MobileInputLog private val buffer: LogBuffer,
+) {
+    fun logOnCapabilitiesChanged(
+        network: Network,
+        networkCapabilities: NetworkCapabilities,
+        isDefaultNetworkCallback: Boolean,
+    ) {
+        LoggerHelper.logOnCapabilitiesChanged(
+            buffer,
+            TAG,
+            network,
+            networkCapabilities,
+            isDefaultNetworkCallback,
+        )
+    }
+
+    fun logOnLost(network: Network) {
+        LoggerHelper.logOnLost(buffer, TAG, network)
+    }
+
+    fun logOnServiceStateChanged(serviceState: ServiceState, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                bool1 = serviceState.isEmergencyOnly
+                bool2 = serviceState.roaming
+                str1 = serviceState.operatorAlphaShort
+            },
+            {
+                "onServiceStateChanged: subId=$int1 emergencyOnly=$bool1 roaming=$bool2" +
+                    " operator=$str1"
+            }
+        )
+    }
+
+    fun logOnSignalStrengthsChanged(signalStrength: SignalStrength, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                str1 = signalStrength.toString()
+            },
+            { "onSignalStrengthsChanged: subId=$int1 strengths=$str1" }
+        )
+    }
+
+    fun logOnDataConnectionStateChanged(dataState: Int, networkType: Int, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                int2 = dataState
+                str1 = networkType.toString()
+            },
+            { "onDataConnectionStateChanged: subId=$int1 dataState=$int2 networkType=$str1" },
+        )
+    }
+
+    fun logOnDataActivity(direction: Int, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                int2 = direction
+            },
+            { "onDataActivity: subId=$int1 direction=$int2" },
+        )
+    }
+
+    fun logOnCarrierNetworkChange(active: Boolean, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                bool1 = active
+            },
+            { "onCarrierNetworkChange: subId=$int1 active=$bool1" },
+        )
+    }
+
+    fun logOnDisplayInfoChanged(displayInfo: TelephonyDisplayInfo, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                str1 = displayInfo.toString()
+            },
+            { "onDisplayInfoChanged: subId=$int1 displayInfo=$str1" },
+        )
+    }
+
+    fun logUiAdapterSubIdsUpdated(subs: List<Int>) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = subs.toString() },
+            { "Sub IDs in MobileUiAdapter updated internally: $str1" },
+        )
+    }
+
+    fun logUiAdapterSubIdsSentToIconController(subs: List<Int>) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = subs.toString() },
+            { "Sub IDs in MobileUiAdapter being sent to icon controller: $str1" },
+        )
+    }
+
+    fun logCarrierConfigChanged(subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { int1 = subId },
+            { "onCarrierConfigChanged: subId=$int1" },
+        )
+    }
+
+    fun logOnDataEnabledChanged(enabled: Boolean, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                bool1 = enabled
+            },
+            { "onDataEnabledChanged: subId=$int1 enabled=$bool1" },
+        )
+    }
+
+    fun logActionCarrierConfigChanged() {
+        buffer.log(TAG, LogLevel.INFO, {}, { "Intent received: ACTION_CARRIER_CONFIG_CHANGED" })
+    }
+
+    fun logDefaultDataSubRatConfig(config: MobileMappings.Config) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = config.toString() },
+            { "defaultDataSubRatConfig: $str1" }
+        )
+    }
+
+    fun logDefaultMobileIconMapping(mapping: Map<String, SignalIcon.MobileIconGroup>) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = mapping.toString() },
+            { "defaultMobileIconMapping: $str1" }
+        )
+    }
+
+    fun logDefaultMobileIconGroup(group: SignalIcon.MobileIconGroup) {
+        buffer.log(TAG, LogLevel.INFO, { str1 = group.name }, { "defaultMobileIconGroup: $str1" })
+    }
+}
+
+private const val TAG = "MobileInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
index ef75713..da63ab1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
@@ -22,8 +22,8 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -55,7 +55,7 @@
     interactor: MobileIconsInteractor,
     private val iconController: StatusBarIconController,
     private val iconsViewModelFactory: MobileIconsViewModel.Factory,
-    private val logger: ConnectivityPipelineLogger,
+    private val logger: MobileInputLogger,
     @Application private val scope: CoroutineScope,
     private val statusBarPipelineFlags: StatusBarPipelineFlags,
 ) : CoreStartable {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 185b668..8cb52af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.StateFlow
@@ -42,7 +41,6 @@
     val subscriptionIdsFlow: StateFlow<List<Int>>,
     private val interactor: MobileIconsInteractor,
     private val airplaneModeInteractor: AirplaneModeInteractor,
-    private val logger: ConnectivityPipelineLogger,
     private val constants: ConnectivityConstants,
     @Application private val scope: CoroutineScope,
     private val statusBarPipelineFlags: StatusBarPipelineFlags,
@@ -83,7 +81,6 @@
     constructor(
         private val interactor: MobileIconsInteractor,
         private val airplaneModeInteractor: AirplaneModeInteractor,
-        private val logger: ConnectivityPipelineLogger,
         private val constants: ConnectivityConstants,
         @Application private val scope: CoroutineScope,
         private val statusBarPipelineFlags: StatusBarPipelineFlags,
@@ -93,7 +90,6 @@
                 subscriptionIdsFlow,
                 interactor,
                 airplaneModeInteractor,
-                logger,
                 constants,
                 scope,
                 statusBarPipelineFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
index 0c9b86c..0fe5329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
 import java.io.PrintWriter
 import javax.inject.Inject
 
@@ -40,7 +39,7 @@
     telephonyManager: TelephonyManager,
 ) : Dumpable {
     init {
-        dumpManager.registerNormalDumpable("${SB_LOGGING_TAG}Constants", this)
+        dumpManager.registerNormalDumpable("ConnectivityConstants", this)
     }
 
     /** True if this device has the capability for data connections and false otherwise. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt
new file mode 100644
index 0000000..95548b8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.SharedConnectivityInputLog
+import javax.inject.Inject
+
+/** Logs for connectivity-related inputs that are shared across wifi, mobile, etc. */
+@SysUISingleton
+class ConnectivityInputLogger
+@Inject
+constructor(
+    @SharedConnectivityInputLog private val buffer: LogBuffer,
+) {
+    fun logTuningChanged(tuningList: String?) {
+        buffer.log(TAG, LogLevel.DEBUG, { str1 = tuningList }, { "onTuningChanged: $str1" })
+    }
+}
+
+private const val TAG = "ConnectivityInputLogger"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
deleted file mode 100644
index 45036969..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.pipeline.shared
-
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.telephony.ServiceState
-import android.telephony.SignalStrength
-import android.telephony.TelephonyDisplayInfo
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.log.dagger.StatusBarConnectivityLog
-import com.android.systemui.plugins.log.LogBuffer
-import com.android.systemui.plugins.log.LogLevel
-import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.toString
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.onEach
-
-@SysUISingleton
-class ConnectivityPipelineLogger
-@Inject
-constructor(
-    @StatusBarConnectivityLog private val buffer: LogBuffer,
-) {
-    /**
-     * Logs a change in one of the **raw inputs** to the connectivity pipeline.
-     *
-     * Use this method for inputs that don't have any extra information besides their callback name.
-     */
-    fun logInputChange(callbackName: String) {
-        buffer.log(SB_LOGGING_TAG, LogLevel.INFO, { str1 = callbackName }, { "Input: $str1" })
-    }
-
-    /** Logs a change in one of the **raw inputs** to the connectivity pipeline. */
-    fun logInputChange(callbackName: String, changeInfo: String?) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                str1 = callbackName
-                str2 = changeInfo
-            },
-            { "Input: $str1: $str2" }
-        )
-    }
-
-    /** Logs a **data transformation** that we performed within the connectivity pipeline. */
-    fun logTransformation(transformationName: String, oldValue: Any?, newValue: Any?) {
-        if (oldValue == newValue) {
-            buffer.log(
-                SB_LOGGING_TAG,
-                LogLevel.INFO,
-                {
-                    str1 = transformationName
-                    str2 = oldValue.toString()
-                },
-                { "Transform: $str1: $str2 (transformation didn't change it)" }
-            )
-        } else {
-            buffer.log(
-                SB_LOGGING_TAG,
-                LogLevel.INFO,
-                {
-                    str1 = transformationName
-                    str2 = oldValue.toString()
-                    str3 = newValue.toString()
-                },
-                { "Transform: $str1: $str2 -> $str3" }
-            )
-        }
-    }
-
-    /** Logs a change in one of the **outputs** to the connectivity pipeline. */
-    fun logOutputChange(outputParamName: String, changeInfo: String) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                str1 = outputParamName
-                str2 = changeInfo
-            },
-            { "Output: $str1: $str2" }
-        )
-    }
-
-    fun logOnCapabilitiesChanged(
-        network: Network,
-        networkCapabilities: NetworkCapabilities,
-        isDefaultNetworkCallback: Boolean,
-    ) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                bool1 = isDefaultNetworkCallback
-                int1 = network.getNetId()
-                str1 = networkCapabilities.toString()
-            },
-            { "onCapabilitiesChanged[default=$bool1]: net=$int1 capabilities=$str1" }
-        )
-    }
-
-    fun logOnLost(network: Network) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            { int1 = network.getNetId() },
-            { "onLost: net=$int1" }
-        )
-    }
-
-    fun logOnServiceStateChanged(serviceState: ServiceState, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                bool1 = serviceState.isEmergencyOnly
-                bool2 = serviceState.roaming
-                str1 = serviceState.operatorAlphaShort
-            },
-            {
-                "onServiceStateChanged: subId=$int1 emergencyOnly=$bool1 roaming=$bool2" +
-                    " operator=$str1"
-            }
-        )
-    }
-
-    fun logOnSignalStrengthsChanged(signalStrength: SignalStrength, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                str1 = signalStrength.toString()
-            },
-            { "onSignalStrengthsChanged: subId=$int1 strengths=$str1" }
-        )
-    }
-
-    fun logOnDataConnectionStateChanged(dataState: Int, networkType: Int, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                int2 = dataState
-                str1 = networkType.toString()
-            },
-            { "onDataConnectionStateChanged: subId=$int1 dataState=$int2 networkType=$str1" },
-        )
-    }
-
-    fun logOnDataActivity(direction: Int, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                int2 = direction
-            },
-            { "onDataActivity: subId=$int1 direction=$int2" },
-        )
-    }
-
-    fun logOnCarrierNetworkChange(active: Boolean, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                bool1 = active
-            },
-            { "onCarrierNetworkChange: subId=$int1 active=$bool1" },
-        )
-    }
-
-    fun logOnDisplayInfoChanged(displayInfo: TelephonyDisplayInfo, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                str1 = displayInfo.toString()
-            },
-            { "onDisplayInfoChanged: subId=$int1 displayInfo=$str1" },
-        )
-    }
-
-    // TODO(b/238425913): We should split this class into mobile-specific and wifi-specific loggers.
-
-    fun logUiAdapterSubIdsUpdated(subs: List<Int>) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            { str1 = subs.toString() },
-            { "Sub IDs in MobileUiAdapter updated internally: $str1" },
-        )
-    }
-
-    fun logUiAdapterSubIdsSentToIconController(subs: List<Int>) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            { str1 = subs.toString() },
-            { "Sub IDs in MobileUiAdapter being sent to icon controller: $str1" },
-        )
-    }
-
-    fun logCarrierConfigChanged(subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            { int1 = subId },
-            { "onCarrierConfigChanged: subId=$int1" },
-        )
-    }
-
-    fun logOnDataEnabledChanged(enabled: Boolean, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                bool1 = enabled
-            },
-            { "onDataEnabledChanged: subId=$int1 enabled=$bool1" },
-        )
-    }
-
-    companion object {
-        const val SB_LOGGING_TAG = "SbConnectivity"
-
-        /** Log a change in one of the **inputs** to the connectivity pipeline. */
-        fun Flow<Unit>.logInputChange(
-            logger: ConnectivityPipelineLogger,
-            inputParamName: String,
-        ): Flow<Unit> {
-            return this.onEach { logger.logInputChange(inputParamName) }
-        }
-
-        /**
-         * Log a change in one of the **inputs** to the connectivity pipeline.
-         *
-         * @param prettyPrint an optional function to transform the value into a readable string.
-         * [toString] is used if no custom function is provided.
-         */
-        fun <T> Flow<T>.logInputChange(
-            logger: ConnectivityPipelineLogger,
-            inputParamName: String,
-            prettyPrint: (T) -> String = { it.toString() }
-        ): Flow<T> {
-            return this.onEach { logger.logInputChange(inputParamName, prettyPrint(it)) }
-        }
-
-        /**
-         * Log a change in one of the **outputs** to the connectivity pipeline.
-         *
-         * @param prettyPrint an optional function to transform the value into a readable string.
-         * [toString] is used if no custom function is provided.
-         */
-        fun <T> Flow<T>.logOutputChange(
-            logger: ConnectivityPipelineLogger,
-            outputParamName: String,
-            prettyPrint: (T) -> String = { it.toString() }
-        ): Flow<T> {
-            return this.onEach { logger.logOutputChange(outputParamName, prettyPrint(it)) }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt
new file mode 100644
index 0000000..6f29e33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared
+
+import android.net.Network
+import android.net.NetworkCapabilities
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+
+/** Helper object for logs that are shared between wifi and mobile. */
+object LoggerHelper {
+    fun logOnCapabilitiesChanged(
+        buffer: LogBuffer,
+        tag: String,
+        network: Network,
+        networkCapabilities: NetworkCapabilities,
+        isDefaultNetworkCallback: Boolean,
+    ) {
+        buffer.log(
+            tag,
+            LogLevel.INFO,
+            {
+                bool1 = isDefaultNetworkCallback
+                int1 = network.getNetId()
+                str1 = networkCapabilities.toString()
+            },
+            { "onCapabilitiesChanged[default=$bool1]: net=$int1 capabilities=$str1" }
+        )
+    }
+
+    fun logOnLost(buffer: LogBuffer, tag: String, network: Network) {
+        buffer.log(tag, LogLevel.INFO, { int1 = network.getNetId() }, { "onLost: net=$int1" })
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
index 45c6d46..5d9ba018 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
@@ -26,8 +26,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.phone.StatusBarIconController
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityInputLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
 import com.android.systemui.tuner.TunerService
@@ -45,59 +44,61 @@
  * types of connectivity (wifi, mobile, ethernet, etc.)
  */
 interface ConnectivityRepository {
-    /**
-     * Observable for the current set of connectivity icons that should be force-hidden.
-     */
+    /** Observable for the current set of connectivity icons that should be force-hidden. */
     val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>>
 }
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
-class ConnectivityRepositoryImpl @Inject constructor(
+class ConnectivityRepositoryImpl
+@Inject
+constructor(
     private val connectivitySlots: ConnectivitySlots,
     context: Context,
     dumpManager: DumpManager,
-    logger: ConnectivityPipelineLogger,
+    logger: ConnectivityInputLogger,
     @Application scope: CoroutineScope,
     tunerService: TunerService,
 ) : ConnectivityRepository, Dumpable {
     init {
-        dumpManager.registerDumpable("${SB_LOGGING_TAG}Repository", this)
+        dumpManager.registerDumpable("ConnectivityRepository", this)
     }
 
     // The default set of hidden icons to use if we don't get any from [TunerService].
     private val defaultHiddenIcons: Set<ConnectivitySlot> =
-            context.resources.getStringArray(DEFAULT_HIDDEN_ICONS_RESOURCE)
-                .asList()
-                .toSlotSet(connectivitySlots)
+        context.resources
+            .getStringArray(DEFAULT_HIDDEN_ICONS_RESOURCE)
+            .asList()
+            .toSlotSet(connectivitySlots)
 
-    override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> = conflatedCallbackFlow {
-        val callback = object : TunerService.Tunable {
-            override fun onTuningChanged(key: String, newHideList: String?) {
-                if (key != HIDDEN_ICONS_TUNABLE_KEY) {
-                    return
-                }
-                logger.logInputChange("onTuningChanged", newHideList)
+    override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : TunerService.Tunable {
+                        override fun onTuningChanged(key: String, newHideList: String?) {
+                            if (key != HIDDEN_ICONS_TUNABLE_KEY) {
+                                return
+                            }
+                            logger.logTuningChanged(newHideList)
 
-                val outputList = newHideList?.split(",")?.toSlotSet(connectivitySlots)
-                    ?: defaultHiddenIcons
-                trySend(outputList)
+                            val outputList =
+                                newHideList?.split(",")?.toSlotSet(connectivitySlots)
+                                    ?: defaultHiddenIcons
+                            trySend(outputList)
+                        }
+                    }
+                tunerService.addTunable(callback, HIDDEN_ICONS_TUNABLE_KEY)
+
+                awaitClose { tunerService.removeTunable(callback) }
             }
-        }
-        tunerService.addTunable(callback, HIDDEN_ICONS_TUNABLE_KEY)
-
-        awaitClose { tunerService.removeTunable(callback) }
-    }
-        .stateIn(
-            scope,
-            started = SharingStarted.WhileSubscribed(),
-            initialValue = defaultHiddenIcons
-        )
+            .stateIn(
+                scope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = defaultHiddenIcons
+            )
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.apply {
-            println("defaultHiddenIcons=$defaultHiddenIcons")
-        }
+        pw.apply { println("defaultHiddenIcons=$defaultHiddenIcons") }
     }
 
     companion object {
@@ -111,8 +112,7 @@
         private fun List<String>.toSlotSet(
             connectivitySlots: ConnectivitySlots
         ): Set<ConnectivitySlot> {
-            return this
-                .filter { it.isNotBlank() }
+            return this.filter { it.isNotBlank() }
                 .mapNotNull { connectivitySlots.getSlotFromName(it) }
                 .toSet()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index 7b486c1..ee58160 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -39,12 +39,11 @@
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import java.util.concurrent.Executor
 import javax.inject.Inject
@@ -58,6 +57,7 @@
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
 /** Real implementation of [WifiRepository]. */
@@ -70,7 +70,7 @@
 constructor(
     broadcastDispatcher: BroadcastDispatcher,
     connectivityManager: ConnectivityManager,
-    logger: ConnectivityPipelineLogger,
+    logger: WifiInputLogger,
     @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
     @Main mainExecutor: Executor,
     @Application scope: CoroutineScope,
@@ -80,7 +80,7 @@
     private val wifiStateChangeEvents: Flow<Unit> =
         broadcastDispatcher
             .broadcastFlow(IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION))
-            .logInputChange(logger, "WIFI_STATE_CHANGED_ACTION intent")
+            .onEach { logger.logIntent("WIFI_STATE_CHANGED_ACTION") }
 
     private val wifiNetworkChangeEvents: MutableSharedFlow<Unit> =
         MutableSharedFlow(extraBufferCapacity = 1)
@@ -173,11 +173,6 @@
                                         networkCapabilities,
                                         wifiManager,
                                     )
-                                logger.logTransformation(
-                                    WIFI_NETWORK_CALLBACK_NAME,
-                                    oldValue = currentWifi,
-                                    newValue = wifiNetworkModel,
-                                )
                                 currentWifi = wifiNetworkModel
                                 trySend(wifiNetworkModel)
                             }
@@ -194,11 +189,6 @@
                                     wifi.networkId == network.getNetId()
                             ) {
                                 val newNetworkModel = WifiNetworkModel.Inactive
-                                logger.logTransformation(
-                                    WIFI_NETWORK_CALLBACK_NAME,
-                                    oldValue = wifi,
-                                    newValue = newNetworkModel,
-                                )
                                 currentWifi = newNetworkModel
                                 trySend(newNetworkModel)
                             }
@@ -228,7 +218,7 @@
     override val wifiActivity: StateFlow<DataActivityModel> =
         conflatedCallbackFlow {
                 val callback = TrafficStateCallback { state ->
-                    logger.logInputChange("onTrafficStateChange", prettyPrintActivity(state))
+                    logger.logActivity(prettyPrintActivity(state))
                     trySend(state.toWifiDataActivityModel())
                 }
                 wifiManager.registerTrafficStateCallback(mainExecutor, callback)
@@ -336,8 +326,6 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .build()
 
-        private const val WIFI_NETWORK_CALLBACK_NAME = "wifiNetworkModel"
-
         private const val CARRIER_MERGED_INVALID_SUB_ID_REASON =
             "Wifi network was carrier merged but had invalid sub ID"
     }
@@ -348,7 +336,7 @@
     constructor(
         private val broadcastDispatcher: BroadcastDispatcher,
         private val connectivityManager: ConnectivityManager,
-        private val logger: ConnectivityPipelineLogger,
+        private val logger: WifiInputLogger,
         @WifiTableLog private val wifiTableLogBuffer: TableLogBuffer,
         @Main private val mainExecutor: Executor,
         @Application private val scope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
index 4f7fe28..8bea772 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
 import java.io.PrintWriter
 import javax.inject.Inject
 
@@ -30,12 +29,14 @@
  * logging purposes.
  */
 @SysUISingleton
-class WifiConstants @Inject constructor(
-        context: Context,
-        dumpManager: DumpManager,
+class WifiConstants
+@Inject
+constructor(
+    context: Context,
+    dumpManager: DumpManager,
 ) : Dumpable {
     init {
-        dumpManager.registerDumpable("${SB_LOGGING_TAG}WifiConstants", this)
+        dumpManager.registerNormalDumpable("WifiConstants", this)
     }
 
     /** True if we should always show the wifi icon when wifi is enabled and false otherwise. */
@@ -43,8 +44,6 @@
         context.resources.getBoolean(R.bool.config_showWifiIndicatorWhenEnabled)
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.apply {
-            println("alwaysShowIconIfEnabled=$alwaysShowIconIfEnabled")
-        }
+        pw.apply { println("alwaysShowIconIfEnabled=$alwaysShowIconIfEnabled") }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
new file mode 100644
index 0000000..a32e475
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.shared
+
+import android.net.Network
+import android.net.NetworkCapabilities
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.WifiInputLog
+import com.android.systemui.statusbar.pipeline.shared.LoggerHelper
+import javax.inject.Inject
+
+/**
+ * Logger for all the wifi-related inputs (intents, callbacks, etc.) that the wifi repo receives.
+ */
+@SysUISingleton
+class WifiInputLogger
+@Inject
+constructor(
+    @WifiInputLog val buffer: LogBuffer,
+) {
+    fun logOnCapabilitiesChanged(
+        network: Network,
+        networkCapabilities: NetworkCapabilities,
+        isDefaultNetworkCallback: Boolean,
+    ) {
+        LoggerHelper.logOnCapabilitiesChanged(
+            buffer,
+            TAG,
+            network,
+            networkCapabilities,
+            isDefaultNetworkCallback,
+        )
+    }
+
+    fun logOnLost(network: Network) {
+        LoggerHelper.logOnLost(buffer, TAG, network)
+    }
+
+    fun logIntent(intentName: String) {
+        buffer.log(TAG, LogLevel.DEBUG, { str1 = intentName }, { "Intent received: $str1" })
+    }
+
+    fun logActivity(activity: String) {
+        buffer.log(TAG, LogLevel.DEBUG, { str1 = activity }, { "Activity: $str1" })
+    }
+}
+
+private const val TAG = "WifiInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index 0f5ff91..1057231 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -34,8 +34,6 @@
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
@@ -69,7 +67,6 @@
     airplaneModeViewModel: AirplaneModeViewModel,
     connectivityConstants: ConnectivityConstants,
     private val context: Context,
-    logger: ConnectivityPipelineLogger,
     @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
     interactor: WifiInteractor,
     @Application private val scope: CoroutineScope,
@@ -143,29 +140,35 @@
             )
 
     /** The wifi activity status. Null if we shouldn't display the activity status. */
-    private val activity: Flow<DataActivityModel?> =
+    private val activity: Flow<DataActivityModel> = run {
+        val default = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
         if (!connectivityConstants.shouldShowActivityConfig) {
-                flowOf(null)
+                flowOf(default)
             } else {
                 combine(interactor.activity, interactor.ssid) { activity, ssid ->
                     when (ssid) {
-                        null -> null
+                        null -> default
                         else -> activity
                     }
                 }
             }
             .distinctUntilChanged()
-            .logOutputChange(logger, "activity")
-            .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null)
+            .logDiffsForTable(
+                wifiTableLogBuffer,
+                columnPrefix = "VM.activity",
+                initialValue = default,
+            )
+            .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = default)
+    }
 
     private val isActivityInViewVisible: Flow<Boolean> =
         activity
-            .map { it?.hasActivityIn == true }
+            .map { it.hasActivityIn }
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
 
     private val isActivityOutViewVisible: Flow<Boolean> =
         activity
-            .map { it?.hasActivityOut == true }
+            .map { it.hasActivityOut }
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
 
     private val isActivityContainerVisible: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index 1ae1eae..8929e02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -56,7 +56,7 @@
     /**
      * Whether the bouncer (PIN/password entry) is currently visible.
      */
-    boolean isBouncerShowing();
+    boolean isPrimaryBouncerShowing();
 
     /**
      * If swiping up will unlock without asking for a password.
@@ -196,7 +196,7 @@
     /** **/
     default void notifyKeyguardState(boolean showing, boolean occluded) {}
     /** **/
-    default void notifyBouncerShowing(boolean showing) {}
+    default void notifyPrimaryBouncerShowing(boolean showing) {}
 
     /**
      * Updates the keyguard state to reflect that it's in the process of being dismissed, either by
@@ -244,7 +244,7 @@
         /**
          * Called when the bouncer (PIN/password entry) is shown or hidden.
          */
-        default void onBouncerShowingChanged() {}
+        default void onPrimaryBouncerShowingChanged() {}
 
         /**
          * Triggered when the device was just unlocked and the lock screen is being dismissed.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 9ad36fd5..805368c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -65,7 +65,7 @@
 
     private boolean mCanDismissLockScreen;
     private boolean mShowing;
-    private boolean mBouncerShowing;
+    private boolean mPrimaryBouncerShowing;
     private boolean mSecure;
     private boolean mOccluded;
 
@@ -157,8 +157,8 @@
     }
 
     @Override
-    public boolean isBouncerShowing() {
-        return mBouncerShowing;
+    public boolean isPrimaryBouncerShowing() {
+        return mPrimaryBouncerShowing;
     }
 
     @Override
@@ -339,11 +339,11 @@
     }
 
     @Override
-    public void notifyBouncerShowing(boolean showing) {
-        if (mBouncerShowing != showing) {
-            mBouncerShowing = showing;
+    public void notifyPrimaryBouncerShowing(boolean showing) {
+        if (mPrimaryBouncerShowing != showing) {
+            mPrimaryBouncerShowing = showing;
 
-            new ArrayList<>(mCallbacks).forEach(Callback::onBouncerShowingChanged);
+            new ArrayList<>(mCallbacks).forEach(Callback::onPrimaryBouncerShowingChanged);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
index c1f0c58..39dda8c 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
+++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
@@ -55,15 +55,15 @@
                 Pair.create("error_container", MDC.errorContainer),
                 Pair.create("on_error_container", MDC.onErrorContainer),
                 Pair.create("primary_fixed", MDC.primaryFixed),
-                Pair.create("primary_fixed_darker", MDC.primaryFixedDarker),
+                Pair.create("primary_fixed_dim", MDC.primaryFixedDim),
                 Pair.create("on_primary_fixed", MDC.onPrimaryFixed),
                 Pair.create("on_primary_fixed_variant", MDC.onPrimaryFixedVariant),
                 Pair.create("secondary_fixed", MDC.secondaryFixed),
-                Pair.create("secondary_fixed_darker", MDC.secondaryFixedDarker),
+                Pair.create("secondary_fixed_dim", MDC.secondaryFixedDim),
                 Pair.create("on_secondary_fixed", MDC.onSecondaryFixed),
                 Pair.create("on_secondary_fixed_variant", MDC.onSecondaryFixedVariant),
                 Pair.create("tertiary_fixed", MDC.tertiaryFixed),
-                Pair.create("tertiary_fixed_darker", MDC.tertiaryFixedDarker),
+                Pair.create("tertiary_fixed_dim", MDC.tertiaryFixedDim),
                 Pair.create("on_tertiary_fixed", MDC.onTertiaryFixed),
                 Pair.create("on_tertiary_fixed_variant", MDC.onTertiaryFixedVariant),
                 Pair.create("control_activated", MDC.controlActivated),
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index badeb27..81fef7a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -31,6 +31,7 @@
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
@@ -338,6 +339,70 @@
     }
 
     @Test
+    fun isWakeupForceDismissKeyguard_singleValue() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN lift is considered an unlock intent
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+            PowerManager.WAKE_REASON_LIFT.toString(), currentUser)
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
+        ))
+
+        // THEN only WAKE_REASON_LIFT is considered an unlock intent
+        for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
+            if (wakeReason == PowerManager.WAKE_REASON_LIFT) {
+                assertTrue(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
+            } else {
+                assertFalse(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
+            }
+        }
+    }
+
+    @Test
+    fun isWakeupForceDismissKeyguard_emptyValues() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN lift and tap are considered an unlock intent
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+            " ", currentUser)
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
+        ))
+
+        // THEN no wake up gestures are considered an unlock intent
+        for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
+            assertFalse(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
+        }
+    }
+
+    @Test
+    fun isWakeupForceDismissKeyguard_multiValue() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN lift and tap are considered an unlock intent
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+            PowerManager.WAKE_REASON_LIFT.toString() +
+                    "|" +
+                    PowerManager.WAKE_REASON_TAP.toString(),
+            currentUser
+        )
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
+        ))
+
+        // THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent
+        for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
+            if (wakeReason == PowerManager.WAKE_REASON_LIFT ||
+                wakeReason == PowerManager.WAKE_REASON_TAP) {
+                assertTrue(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
+            } else {
+                assertFalse(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
+            }
+        }
+    }
+
+    @Test
     fun dump_onUnlockIntentWhenBiometricEnrolled_invalidNum_noArrayOutOfBoundsException() {
         // GIVEN an invalid input (-1)
         secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index f7fec80..480b8f9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -85,6 +86,7 @@
     @Mock private lateinit var transitionRepository: KeyguardTransitionRepository
     @Mock private lateinit var commandQueue: CommandQueue
     private lateinit var repository: FakeKeyguardRepository
+    private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
     @Mock private lateinit var smallLogBuffer: LogBuffer
     @Mock private lateinit var largeLogBuffer: LogBuffer
     private lateinit var underTest: ClockEventController
@@ -103,11 +105,15 @@
         whenever(largeClockEvents.tickRate).thenReturn(ClockTickRate.PER_MINUTE)
 
         repository = FakeKeyguardRepository()
+        bouncerRepository = FakeKeyguardBouncerRepository()
 
         underTest = ClockEventController(
-            KeyguardInteractor(repository = repository,
-                    commandQueue = commandQueue,
-                    featureFlags = featureFlags),
+            KeyguardInteractor(
+                repository = repository,
+                commandQueue = commandQueue,
+                featureFlags = featureFlags,
+                bouncerRepository = bouncerRepository,
+            ),
             KeyguardTransitionInteractor(repository = transitionRepository),
             broadcastDispatcher,
             batteryController,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index ccc4e4a..a5f90f8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -241,7 +241,7 @@
         mController.init();
         verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
 
-        listenerArgumentCaptor.getValue().onClockChanged();
+        listenerArgumentCaptor.getValue().onCurrentClockChanged();
         verify(mView, times(2)).setClock(mClockController, StatusBarState.SHADE);
         verify(mClockEventController, times(2)).setClock(mClockController);
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 841ec4b..9fe7506 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -909,6 +909,25 @@
     }
 
     @Test
+    public void noFpListeningWhenKeyguardIsOccluded_unlessAlternateBouncerShowing() {
+        // GIVEN device is awake but occluded
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+        mKeyguardUpdateMonitor.setKeyguardShowing(false, true);
+
+        // THEN fingerprint shouldn't listen
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse();
+        verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+                anyInt(), anyInt());
+
+        // WHEN alternate bouncer is shown
+        mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
+
+        // THEN make sure FP listening begins
+        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+                anyInt());
+    }
+
+    @Test
     public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() {
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
@@ -2006,7 +2025,6 @@
         verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
     }
 
-
     @Test
     public void testOnTrustGrantedForCurrentUser_dismissKeyguardRequested_deviceInteractive() {
         // GIVEN device is interactive
@@ -2432,6 +2450,56 @@
         assertThat(mKeyguardUpdateMonitor.mIncompatibleCharger).isFalse();
     }
 
+    @Test
+    public void unfoldWakeup_requestActiveUnlock_forceDismissKeyguard()
+            throws RemoteException {
+        // GIVEN shouldTriggerActiveUnlock
+        keyguardIsVisible();
+        when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+        // GIVEN active unlock triggers on wakeup
+        when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
+                .thenReturn(true);
+
+        // GIVEN an unfold should force dismiss the keyguard
+        when(mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(
+                PowerManager.WAKE_REASON_UNFOLD_DEVICE)).thenReturn(true);
+
+        // WHEN device wakes up from an unfold
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNFOLD_DEVICE);
+        mTestableLooper.processAllMessages();
+
+        // THEN request unlock with a keyguard dismissal
+        verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+                eq(true));
+    }
+
+    @Test
+    public void unfoldWakeup_requestActiveUnlock_noDismissKeyguard()
+            throws RemoteException {
+        // GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE
+        keyguardIsVisible();
+        when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+        // GIVEN active unlock triggers on wakeup
+        when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
+                .thenReturn(true);
+
+        // GIVEN an unfold should NOT force dismiss the keyguard
+        when(mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(
+                PowerManager.WAKE_REASON_UNFOLD_DEVICE)).thenReturn(false);
+
+        // WHEN device wakes up from an unfold
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNFOLD_DEVICE);
+        mTestableLooper.processAllMessages();
+
+        // THEN request unlock WITHOUT a keyguard dismissal
+        verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+                eq(false));
+    }
+
     private void userDeviceLockDown() {
         when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
         when(mStrongAuthTracker.getStrongAuthForUser(mCurrentUserId))
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 3d0d036..456702b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -43,6 +43,7 @@
 import com.android.systemui.doze.util.BurnInHelperKt;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository;
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -159,9 +160,12 @@
                 mAuthRippleController,
                 mResources,
                 new KeyguardTransitionInteractor(mTransitionRepository),
-                new KeyguardInteractor(new FakeKeyguardRepository(),
+                new KeyguardInteractor(
+                        new FakeKeyguardRepository(),
                         mCommandQueue,
-                        mFeatureFlags),
+                        mFeatureFlags,
+                        new FakeKeyguardBouncerRepository()
+                ),
                 mFeatureFlags
         );
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index 472d207..f370ad6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -286,4 +286,26 @@
         // THEN the lock icon is shown
         verify(mLockIconView).setContentDescription(LOCKED_LABEL);
     }
+
+    @Test
+    public void lockIconShows_afterUnlockStateChanges() {
+        // GIVEN lock icon controller is initialized and view is attached
+        init(/* useMigrationFlag= */false);
+        captureKeyguardStateCallback();
+        captureKeyguardUpdateMonitorCallback();
+
+        // GIVEN user has unlocked with a biometric auth (ie: face auth)
+        // and biometric running state changes
+        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
+        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
+                BiometricSourceType.FACE);
+        reset(mLockIconView);
+
+        // WHEN the unlocked state changes
+        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
+        mKeyguardStateCallback.onUnlockedChanged();
+
+        // THEN the lock icon is shown
+        verify(mLockIconView).setContentDescription(LOCKED_LABEL);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
index 4439586..2283746 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
@@ -18,9 +18,13 @@
 
 import android.app.job.JobParameters
 import android.content.Context
+import android.os.PersistableBundle
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper.DeletionJobService.Companion.USER
+import com.android.systemui.util.mockito.whenever
+import java.util.concurrent.TimeUnit
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -28,18 +32,15 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
-import java.util.concurrent.TimeUnit
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class DeletionJobServiceTest : SysuiTestCase() {
 
-    @Mock
-    private lateinit var context: Context
+    @Mock private lateinit var context: Context
 
     private lateinit var service: AuxiliaryPersistenceWrapper.DeletionJobService
 
@@ -53,6 +54,10 @@
 
     @Test
     fun testOnStartJob() {
+        val bundle = PersistableBundle().also { it.putInt(USER, 0) }
+        val params = mock(JobParameters::class.java)
+        whenever(params.getExtras()).thenReturn(bundle)
+
         // false means job is terminated
         assertFalse(service.onStartJob(mock(JobParameters::class.java)))
         verify(context).deleteFile(AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME)
@@ -67,13 +72,17 @@
     @Test
     fun testJobHasRightParameters() {
         val userId = 10
-        `when`(context.userId).thenReturn(userId)
-        `when`(context.packageName).thenReturn(mContext.packageName)
+        whenever(context.userId).thenReturn(userId)
+        whenever(context.packageName).thenReturn(mContext.packageName)
 
-        val jobInfo = AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context)
+        val jobInfo =
+            AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context, userId)
         assertEquals(
-            AuxiliaryPersistenceWrapper.DeletionJobService.DELETE_FILE_JOB_ID + userId, jobInfo.id)
+            AuxiliaryPersistenceWrapper.DeletionJobService.DELETE_FILE_JOB_ID + userId,
+            jobInfo.id
+        )
         assertTrue(jobInfo.isPersisted)
+        assertEquals(userId, jobInfo.getExtras().getInt(USER))
         assertEquals(TimeUnit.DAYS.toMillis(7), jobInfo.minLatencyMillis)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 4415033..15a454b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -172,6 +173,7 @@
                         repository = FakeKeyguardRepository(),
                         commandQueue = commandQueue,
                         featureFlags = featureFlags,
+                        bouncerRepository = FakeKeyguardBouncerRepository(),
                     ),
                 registry = mock(),
                 lockPatternUtils = lockPatternUtils,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 8bb6a85..0469e77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -324,29 +324,6 @@
         }
 
     @Test
-    fun isBouncerShowing() =
-        runTest(UnconfinedTestDispatcher()) {
-            whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
-            var latest: Boolean? = null
-            val job = underTest.isBouncerShowing.onEach { latest = it }.launchIn(this)
-
-            assertThat(latest).isFalse()
-
-            val captor = argumentCaptor<KeyguardStateController.Callback>()
-            verify(keyguardStateController).addCallback(captor.capture())
-
-            whenever(keyguardStateController.isBouncerShowing).thenReturn(true)
-            captor.value.onBouncerShowingChanged()
-            assertThat(latest).isTrue()
-
-            whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
-            captor.value.onBouncerShowingChanged()
-            assertThat(latest).isFalse()
-
-            job.cancel()
-        }
-
-    @Test
     fun isKeyguardGoingAway() =
         runTest(UnconfinedTestDispatcher()) {
             whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
index 7ded354..18e80ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -130,7 +130,7 @@
         givenCanShowAlternateBouncer()
 
         assertTrue(underTest.show())
-        assertTrue(bouncerRepository.isAlternateBouncerVisible.value)
+        assertTrue(bouncerRepository.alternateBouncerVisible.value)
     }
 
     @Test
@@ -138,7 +138,7 @@
         givenCannotShowAlternateBouncer()
 
         assertFalse(underTest.show())
-        assertFalse(bouncerRepository.isAlternateBouncerVisible.value)
+        assertFalse(bouncerRepository.alternateBouncerVisible.value)
     }
 
     @Test
@@ -146,7 +146,7 @@
         bouncerRepository.setAlternateVisible(true)
 
         assertTrue(underTest.hide())
-        assertFalse(bouncerRepository.isAlternateBouncerVisible.value)
+        assertFalse(bouncerRepository.alternateBouncerVisible.value)
     }
 
     @Test
@@ -154,7 +154,7 @@
         bouncerRepository.setAlternateVisible(false)
 
         assertFalse(underTest.hide())
-        assertFalse(bouncerRepository.isAlternateBouncerVisible.value)
+        assertFalse(bouncerRepository.alternateBouncerVisible.value)
     }
 
     private fun givenCanShowAlternateBouncer() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 7d4861b..153439e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
 import com.android.systemui.settings.DisplayTracker
@@ -38,7 +39,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
-import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -50,6 +50,7 @@
 
     private lateinit var underTest: KeyguardInteractor
     private lateinit var repository: FakeKeyguardRepository
+    private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
 
     @Before
     fun setUp() {
@@ -58,7 +59,14 @@
         commandQueue = FakeCommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java))
         testScope = TestScope()
         repository = FakeKeyguardRepository()
-        underTest = KeyguardInteractor(repository, commandQueue, featureFlags)
+        bouncerRepository = FakeKeyguardBouncerRepository()
+        underTest =
+            KeyguardInteractor(
+                repository,
+                commandQueue,
+                featureFlags,
+                bouncerRepository,
+            )
     }
 
     @Test
@@ -137,7 +145,7 @@
             repository.setKeyguardOccluded(true)
             assertThat(secureCameraActive()).isTrue()
 
-            repository.setBouncerShowing(true)
+            bouncerRepository.setPrimaryVisible(true)
             assertThat(secureCameraActive()).isFalse()
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 240af7b..23e06ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
@@ -298,6 +299,7 @@
                         repository = FakeKeyguardRepository(),
                         commandQueue = commandQueue,
                         featureFlags = featureFlags,
+                        bouncerRepository = FakeKeyguardBouncerRepository(),
                     ),
                 registry =
                     FakeKeyguardQuickAffordanceRegistry(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index ec70857..1b8c627 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
@@ -159,7 +160,8 @@
                     KeyguardInteractor(
                         repository = repository,
                         commandQueue = commandQueue,
-                        featureFlags = featureFlags
+                        featureFlags = featureFlags,
+                        bouncerRepository = FakeKeyguardBouncerRepository(),
                     ),
                 registry =
                     FakeKeyguardQuickAffordanceRegistry(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 46e4679..ae7a928 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -22,7 +22,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl
@@ -65,6 +67,7 @@
     private lateinit var testScope: TestScope
 
     private lateinit var keyguardRepository: FakeKeyguardRepository
+    private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
     private lateinit var shadeRepository: ShadeRepository
 
     // Used to issue real transition steps for test input
@@ -81,6 +84,10 @@
     private lateinit var fromOccludedTransitionInteractor: FromOccludedTransitionInteractor
     private lateinit var fromGoneTransitionInteractor: FromGoneTransitionInteractor
     private lateinit var fromAodTransitionInteractor: FromAodTransitionInteractor
+    private lateinit var fromAlternateBouncerTransitionInteractor:
+        FromAlternateBouncerTransitionInteractor
+    private lateinit var fromPrimaryBouncerTransitionInteractor:
+        FromPrimaryBouncerTransitionInteractor
 
     @Before
     fun setUp() {
@@ -88,6 +95,7 @@
         testScope = TestScope()
 
         keyguardRepository = FakeKeyguardRepository()
+        bouncerRepository = FakeKeyguardBouncerRepository()
         shadeRepository = FakeShadeRepository()
 
         /* Used to issue full transition steps, to better simulate a real device */
@@ -98,8 +106,7 @@
         fromLockscreenTransitionInteractor =
             FromLockscreenTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor =
-                    KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 shadeRepository = shadeRepository,
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
@@ -109,8 +116,7 @@
         fromDreamingTransitionInteractor =
             FromDreamingTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor =
-                    KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
             )
@@ -119,8 +125,7 @@
         fromAodTransitionInteractor =
             FromAodTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor =
-                    KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
             )
@@ -129,8 +134,7 @@
         fromGoneTransitionInteractor =
             FromGoneTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor =
-                    KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
             )
@@ -139,8 +143,7 @@
         fromDozingTransitionInteractor =
             FromDozingTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor =
-                    KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
             )
@@ -149,12 +152,29 @@
         fromOccludedTransitionInteractor =
             FromOccludedTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor =
-                    KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
             )
         fromOccludedTransitionInteractor.start()
+
+        fromAlternateBouncerTransitionInteractor =
+            FromAlternateBouncerTransitionInteractor(
+                scope = testScope,
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
+                keyguardTransitionRepository = mockTransitionRepository,
+                keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+            )
+        fromAlternateBouncerTransitionInteractor.start()
+
+        fromPrimaryBouncerTransitionInteractor =
+            FromPrimaryBouncerTransitionInteractor(
+                scope = testScope,
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
+                keyguardTransitionRepository = mockTransitionRepository,
+                keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+            )
+        fromPrimaryBouncerTransitionInteractor.start()
     }
 
     @Test
@@ -256,7 +276,7 @@
         }
 
     @Test
-    fun `LOCKSCREEN to BOUNCER via bouncer showing call`() =
+    fun `LOCKSCREEN to PRIMARY_BOUNCER via bouncer showing call`() =
         testScope.runTest {
             // GIVEN a device that has at least woken up
             keyguardRepository.setWakefulnessModel(startingToWake())
@@ -278,18 +298,18 @@
             )
             runCurrent()
 
-            // WHEN the bouncer is set to show
-            keyguardRepository.setBouncerShowing(true)
+            // WHEN the primary bouncer is set to show
+            bouncerRepository.setPrimaryVisible(true)
             runCurrent()
 
             val info =
                 withArgCaptor<TransitionInfo> {
                     verify(mockTransitionRepository).startTransition(capture())
                 }
-            // THEN a transition to BOUNCER should occur
+            // THEN a transition to PRIMARY_BOUNCER should occur
             assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
             assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
-            assertThat(info.to).isEqualTo(KeyguardState.BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
             assertThat(info.animator).isNotNull()
 
             coroutineContext.cancelChildren()
@@ -695,6 +715,297 @@
             coroutineContext.cancelChildren()
         }
 
+    @Test
+    fun `ALTERNATE_BOUNCER to PRIMARY_BOUNCER`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to ALTERNATE_BOUNCER
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.ALTERNATE_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN the alternateBouncer stops showing and then the primary bouncer shows
+            bouncerRepository.setPrimaryVisible(true)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to PRIMARY_BOUNCER should occur
+            assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `ALTERNATE_BOUNCER to AOD`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to ALTERNATE_BOUNCER
+            bouncerRepository.setAlternateVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.ALTERNATE_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN the primary bouncer isn't showing, aod available and starting to sleep
+            bouncerRepository.setPrimaryVisible(false)
+            keyguardRepository.setAodAvailable(true)
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+
+            // WHEN the alternateBouncer stops showing
+            bouncerRepository.setAlternateVisible(false)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to AOD should occur
+            assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.AOD)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `ALTERNATE_BOUNCER to DOZING`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to ALTERNATE_BOUNCER
+            bouncerRepository.setAlternateVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.ALTERNATE_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN the primary bouncer isn't showing, aod not available and starting to sleep
+            // to sleep
+            bouncerRepository.setPrimaryVisible(false)
+            keyguardRepository.setAodAvailable(false)
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+
+            // WHEN the alternateBouncer stops showing
+            bouncerRepository.setAlternateVisible(false)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.DOZING)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `ALTERNATE_BOUNCER to LOCKSCREEN`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to ALTERNATE_BOUNCER
+            bouncerRepository.setAlternateVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.ALTERNATE_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN the primary bouncer isn't showing and device not sleeping
+            bouncerRepository.setPrimaryVisible(false)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+
+            // WHEN the alternateBouncer stops showing
+            bouncerRepository.setAlternateVisible(false)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to LOCKSCREEN should occur
+            assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `PRIMARY_BOUNCER to AOD`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to PRIMARY_BOUNCER
+            bouncerRepository.setPrimaryVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.PRIMARY_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN aod available and starting to sleep
+            keyguardRepository.setAodAvailable(true)
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+
+            // WHEN the primaryBouncer stops showing
+            bouncerRepository.setPrimaryVisible(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to AOD should occur
+            assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.AOD)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `PRIMARY_BOUNCER to DOZING`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to PRIMARY_BOUNCER
+            bouncerRepository.setPrimaryVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.PRIMARY_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN aod not available and starting to sleep to sleep
+            keyguardRepository.setAodAvailable(false)
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+
+            // WHEN the primaryBouncer stops showing
+            bouncerRepository.setPrimaryVisible(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.DOZING)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `PRIMARY_BOUNCER to LOCKSCREEN`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to PRIMARY_BOUNCER
+            bouncerRepository.setPrimaryVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.PRIMARY_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN device not sleeping
+            keyguardRepository.setWakefulnessModel(startingToWake())
+
+            // WHEN the alternateBouncer stops showing
+            bouncerRepository.setPrimaryVisible(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to LOCKSCREEN should occur
+            assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
     private fun startingToWake() =
         WakefulnessModel(
             WakefulnessState.STARTING_TO_WAKE,
@@ -710,4 +1021,13 @@
             WakeSleepReason.OTHER,
             WakeSleepReason.OTHER
         )
+
+    private fun createKeyguardInteractor(featureFlags: FeatureFlags): KeyguardInteractor {
+        return KeyguardInteractor(
+            keyguardRepository,
+            commandQueue,
+            featureFlags,
+            bouncerRepository,
+        )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
index c5e0252..46ed829 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -102,7 +102,7 @@
         verify(repository).setPrimaryScrimmed(true)
         verify(repository).setPanelExpansion(EXPANSION_VISIBLE)
         verify(repository).setPrimaryShowingSoon(true)
-        verify(keyguardStateController).notifyBouncerShowing(true)
+        verify(keyguardStateController).notifyPrimaryBouncerShowing(true)
         verify(mPrimaryBouncerCallbackInteractor).dispatchStartingToShow()
         verify(repository).setPrimaryVisible(true)
         verify(repository).setPrimaryShow(any(KeyguardBouncerModel::class.java))
@@ -118,7 +118,7 @@
     @Test
     fun testShow_keyguardIsDone() {
         `when`(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true)
-        verify(keyguardStateController, never()).notifyBouncerShowing(true)
+        verify(keyguardStateController, never()).notifyPrimaryBouncerShowing(true)
         verify(mPrimaryBouncerCallbackInteractor, never()).dispatchStartingToShow()
     }
 
@@ -126,7 +126,7 @@
     fun testHide() {
         underTest.hide()
         verify(falsingCollector).onBouncerHidden()
-        verify(keyguardStateController).notifyBouncerShowing(false)
+        verify(keyguardStateController).notifyPrimaryBouncerShowing(false)
         verify(repository).setPrimaryShowingSoon(false)
         verify(repository).setPrimaryVisible(false)
         verify(repository).setPrimaryHide(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 03a347e..6afeddd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
@@ -136,6 +137,7 @@
                 repository = repository,
                 commandQueue = commandQueue,
                 featureFlags = featureFlags,
+                bouncerRepository = FakeKeyguardBouncerRepository(),
             )
         whenever(userTracker.userHandle).thenReturn(mock())
         whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
index 020a866..3fd19ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
@@ -30,12 +30,14 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.nio.file.Files
 import java.util.concurrent.Executor
-import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
 import org.mockito.Mockito.isNull
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
@@ -61,36 +63,83 @@
             UserFileManagerImpl(context, userManager, broadcastDispatcher, backgroundExecutor)
     }
 
-    @After
-    fun end() {
-        val dir = Environment.buildPath(context.filesDir, UserFileManagerImpl.ID)
-        dir.deleteRecursively()
-    }
-
     @Test
     fun testGetFile() {
         assertThat(userFileManager.getFile(TEST_FILE_NAME, 0).path)
             .isEqualTo("${context.filesDir}/$TEST_FILE_NAME")
         assertThat(userFileManager.getFile(TEST_FILE_NAME, 11).path)
-            .isEqualTo("${context.filesDir}/${UserFileManagerImpl.ID}/11/files/$TEST_FILE_NAME")
+            .isEqualTo("${context.filesDir}/__USER_11_$TEST_FILE_NAME")
     }
 
     @Test
     fun testGetSharedPreferences() {
+        val primarySharedPref = userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0)
         val secondarySharedPref = userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11)
-        val secondaryUserDir =
-            Environment.buildPath(
-                context.filesDir,
-                UserFileManagerImpl.ID,
-                "11",
-                UserFileManagerImpl.SHARED_PREFS,
-                TEST_FILE_NAME
-            )
 
-        assertThat(secondarySharedPref).isNotNull()
-        assertThat(secondaryUserDir.exists())
-        assertThat(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0))
-            .isNotEqualTo(secondarySharedPref)
+        assertThat(primarySharedPref).isNotEqualTo(secondarySharedPref)
+
+        // Make sure these are different files
+        primarySharedPref.edit().putString("TEST", "ABC").commit()
+        assertThat(secondarySharedPref.getString("TEST", null)).isNull()
+
+        context.deleteSharedPreferences("TEST")
+        context.deleteSharedPreferences("__USER_11_TEST")
+    }
+
+    @Test
+    fun testMigrateFile() {
+        val userId = 12
+        val fileName = "myFile.txt"
+        val fileContents = "TestingFile"
+        val legacyFile =
+            UserFileManagerImpl.createLegacyFile(
+                context,
+                UserFileManagerImpl.FILES,
+                fileName,
+                userId
+            )!!
+
+        // Write file to legacy area
+        Files.createDirectories(legacyFile.getParentFile().toPath())
+        Files.write(legacyFile.toPath(), fileContents.toByteArray())
+        assertThat(legacyFile.exists()).isTrue()
+
+        // getFile() should migrate the legacy file to the new location
+        val file = userFileManager.getFile(fileName, userId)
+        val newContents = String(Files.readAllBytes(file.toPath()))
+
+        assertThat(newContents).isEqualTo(fileContents)
+        assertThat(legacyFile.exists()).isFalse()
+        assertThat(File(context.filesDir, UserFileManagerImpl.ROOT_DIR).exists()).isFalse()
+    }
+
+    @Test
+    fun testMigrateSharedPrefs() {
+        val userId = 13
+        val fileName = "myFile"
+        val contents = "TestingSharedPrefs"
+        val legacyFile =
+            UserFileManagerImpl.createLegacyFile(
+                context,
+                UserFileManagerImpl.SHARED_PREFS,
+                "$fileName.xml",
+                userId
+            )!!
+
+        // Write a valid shared prefs xml file to legacy area
+        val tmpPrefs = context.getSharedPreferences("tmp", Context.MODE_PRIVATE)
+        tmpPrefs.edit().putString(contents, contents).commit()
+        Files.createDirectories(legacyFile.getParentFile().toPath())
+        val tmpFile =
+            Environment.buildPath(context.dataDir, UserFileManagerImpl.SHARED_PREFS, "tmp.xml")
+        tmpFile.renameTo(legacyFile)
+        assertThat(legacyFile.exists()).isTrue()
+
+        // getSharedpreferences() should migrate the legacy file to the new location
+        val prefs = userFileManager.getSharedPreferences(fileName, Context.MODE_PRIVATE, userId)
+        assertThat(prefs.getString(contents, "")).isEqualTo(contents)
+        assertThat(legacyFile.exists()).isFalse()
+        assertThat(File(context.filesDir, UserFileManagerImpl.ROOT_DIR).exists()).isFalse()
     }
 
     @Test
@@ -111,44 +160,14 @@
 
     @Test
     fun testClearDeletedUserData() {
-        val dir = Environment.buildPath(context.filesDir, UserFileManagerImpl.ID, "11", "files")
-        dir.mkdirs()
-        val file =
-            Environment.buildPath(
-                context.filesDir,
-                UserFileManagerImpl.ID,
-                "11",
-                "files",
-                TEST_FILE_NAME
-            )
-        val secondaryUserDir =
-            Environment.buildPath(
-                context.filesDir,
-                UserFileManagerImpl.ID,
-                "11",
-            )
+        val file = userFileManager.getFile(TEST_FILE_NAME, 11)
         file.createNewFile()
-        assertThat(secondaryUserDir.exists()).isTrue()
+
         assertThat(file.exists()).isTrue()
         userFileManager.clearDeletedUserData()
         assertThat(backgroundExecutor.runAllReady()).isGreaterThan(0)
-        verify(userManager).aliveUsers
-        assertThat(secondaryUserDir.exists()).isFalse()
-        assertThat(file.exists()).isFalse()
-    }
+        verify(userManager, atLeastOnce()).aliveUsers
 
-    @Test
-    fun testEnsureParentDirExists() {
-        val file =
-            Environment.buildPath(
-                context.filesDir,
-                UserFileManagerImpl.ID,
-                "11",
-                "files",
-                TEST_FILE_NAME
-            )
-        assertThat(file.parentFile.exists()).isFalse()
-        UserFileManagerImpl.ensureParentDirExists(file)
-        assertThat(file.parentFile.exists()).isTrue()
+        assertThat(file.exists()).isFalse()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index d01edcc..26eff61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -230,7 +230,7 @@
     }
 
     @Test
-    fun pluginRemoved_clockChanged() {
+    fun pluginRemoved_clockAndListChanged() {
         val plugin1 = FakeClockPlugin()
             .addClock("clock_1", "clock 1")
             .addClock("clock_2", "clock 2")
@@ -239,20 +239,36 @@
             .addClock("clock_3", "clock 3", { mockClock })
             .addClock("clock_4", "clock 4")
 
-        registry.applySettings(ClockSettings("clock_3", null))
-        pluginListener.onPluginConnected(plugin1, mockContext)
-        pluginListener.onPluginConnected(plugin2, mockContext)
 
         var changeCallCount = 0
-        registry.registerClockChangeListener { changeCallCount++ }
+        var listChangeCallCount = 0
+        registry.registerClockChangeListener(object : ClockRegistry.ClockChangeListener {
+            override fun onCurrentClockChanged() { changeCallCount++ }
+            override fun onAvailableClocksChanged() { listChangeCallCount++ }
+        })
+
+        registry.applySettings(ClockSettings("clock_3", null))
+        assertEquals(0, changeCallCount)
+        assertEquals(0, listChangeCallCount)
+
+        pluginListener.onPluginConnected(plugin1, mockContext)
+        assertEquals(0, changeCallCount)
+        assertEquals(1, listChangeCallCount)
+
+        pluginListener.onPluginConnected(plugin2, mockContext)
+        assertEquals(1, changeCallCount)
+        assertEquals(2, listChangeCallCount)
 
         pluginListener.onPluginDisconnected(plugin1)
-        assertEquals(0, changeCallCount)
+        assertEquals(1, changeCallCount)
+        assertEquals(3, listChangeCallCount)
 
         pluginListener.onPluginDisconnected(plugin2)
-        assertEquals(1, changeCallCount)
+        assertEquals(2, changeCallCount)
+        assertEquals(4, listChangeCallCount)
     }
 
+
     @Test
     fun jsonDeserialization_gotExpectedObject() {
         val expected = ClockSettings("ID", null).apply { _applied_timestamp = 500 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 5832569..4d9db8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -518,7 +518,7 @@
         val childHunView = createHunViewMock(
                 isShadeOpen = true,
                 fullyVisible = false,
-                headerVisibleAmount = 1f,
+                headerVisibleAmount = 1f
         )
         val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
         algorithmState.visibleChildren.add(childHunView)
@@ -526,6 +526,7 @@
         // When: updateChildZValue() is called for the top HUN
         stackScrollAlgorithm.updateChildZValue(
                 /* i= */ 0,
+                /* childrenOnTop= */ 0.0f,
                 /* StackScrollAlgorithmState= */ algorithmState,
                 /* ambientState= */ ambientState,
                 /* shouldElevateHun= */ true
@@ -545,7 +546,7 @@
         val childHunView = createHunViewMock(
                 isShadeOpen = true,
                 fullyVisible = false,
-                headerVisibleAmount = 1f,
+                headerVisibleAmount = 1f
         )
         // Use half of the HUN's height as overlap
         childHunView.viewState.yTranslation = (childHunView.viewState.height + 1 shr 1).toFloat()
@@ -555,6 +556,7 @@
         // When: updateChildZValue() is called for the top HUN
         stackScrollAlgorithm.updateChildZValue(
                 /* i= */ 0,
+                /* childrenOnTop= */ 0.0f,
                 /* StackScrollAlgorithmState= */ algorithmState,
                 /* ambientState= */ ambientState,
                 /* shouldElevateHun= */ true
@@ -578,7 +580,7 @@
         val childHunView = createHunViewMock(
                 isShadeOpen = true,
                 fullyVisible = true,
-                headerVisibleAmount = 1f,
+                headerVisibleAmount = 1f
         )
         // HUN doesn't overlap with QQS Panel
         childHunView.viewState.yTranslation = ambientState.topPadding +
@@ -589,6 +591,7 @@
         // When: updateChildZValue() is called for the top HUN
         stackScrollAlgorithm.updateChildZValue(
                 /* i= */ 0,
+                /* childrenOnTop= */ 0.0f,
                 /* StackScrollAlgorithmState= */ algorithmState,
                 /* ambientState= */ ambientState,
                 /* shouldElevateHun= */ true
@@ -608,7 +611,7 @@
         val childHunView = createHunViewMock(
                 isShadeOpen = false,
                 fullyVisible = false,
-                headerVisibleAmount = 0f,
+                headerVisibleAmount = 0f
         )
         childHunView.viewState.yTranslation = 0f
         // Shade is closed, thus childHunView's headerVisibleAmount is 0
@@ -619,6 +622,7 @@
         // When: updateChildZValue() is called for the top HUN
         stackScrollAlgorithm.updateChildZValue(
                 /* i= */ 0,
+                /* childrenOnTop= */ 0.0f,
                 /* StackScrollAlgorithmState= */ algorithmState,
                 /* ambientState= */ ambientState,
                 /* shouldElevateHun= */ true
@@ -638,7 +642,7 @@
         val childHunView = createHunViewMock(
                 isShadeOpen = false,
                 fullyVisible = false,
-                headerVisibleAmount = 0.5f,
+                headerVisibleAmount = 0.5f
         )
         childHunView.viewState.yTranslation = 0f
         // Shade is being opened, thus childHunView's headerVisibleAmount is between 0 and 1
@@ -650,6 +654,7 @@
         // When: updateChildZValue() is called for the top HUN
         stackScrollAlgorithm.updateChildZValue(
                 /* i= */ 0,
+                /* childrenOnTop= */ 0.0f,
                 /* StackScrollAlgorithmState= */ algorithmState,
                 /* ambientState= */ ambientState,
                 /* shouldElevateHun= */ true
@@ -664,7 +669,7 @@
     private fun createHunViewMock(
             isShadeOpen: Boolean,
             fullyVisible: Boolean,
-            headerVisibleAmount: Float,
+            headerVisibleAmount: Float
     ) =
             mock<ExpandableNotificationRow>().apply {
                 val childViewStateMock = createHunChildViewState(isShadeOpen, fullyVisible)
@@ -675,10 +680,7 @@
             }
 
 
-    private fun createHunChildViewState(
-            isShadeOpen: Boolean,
-            fullyVisible: Boolean,
-    ) =
+    private fun createHunChildViewState(isShadeOpen: Boolean, fullyVisible: Boolean) =
             ExpandableViewState().apply {
                 // Mock the HUN's height with ambientState.topPadding +
                 // ambientState.stackTranslation
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
index a986777..c669c6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
@@ -53,7 +53,7 @@
     }
 
     @Override
-    public boolean isBouncerShowing() {
+    public boolean isPrimaryBouncerShowing() {
         return false;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
index 5a6bb30..ab888f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
@@ -18,9 +18,9 @@
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.google.common.truth.Truth.assertThat
@@ -42,7 +42,7 @@
 
     private lateinit var underTest: AirplaneModeViewModelImpl
 
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: TableLogBuffer
     private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
     private lateinit var connectivityRepository: FakeConnectivityRepository
     private lateinit var interactor: AirplaneModeInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
index 521c67f..0145103 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -53,7 +53,7 @@
     private lateinit var mockitoSession: MockitoSession
     private lateinit var carrierConfigCoreStartable: CarrierConfigCoreStartable
 
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: MobileInputLogger
     @Mock private lateinit var carrierConfigManager: CarrierConfigManager
     @Mock private lateinit var dumpManager: DumpManager
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 4da2104..17502f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -33,8 +33,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.validMobileEvent
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
 import com.android.systemui.util.mockito.any
@@ -81,7 +81,7 @@
     @Mock private lateinit var connectivityManager: ConnectivityManager
     @Mock private lateinit var subscriptionManager: SubscriptionManager
     @Mock private lateinit var telephonyManager: TelephonyManager
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: MobileInputLogger
     @Mock private lateinit var summaryLogger: TableLogBuffer
     @Mock private lateinit var demoModeController: DemoModeController
     @Mock private lateinit var dumpManager: DumpManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index a294088a..b2577e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -65,8 +65,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
 import com.android.systemui.util.mockito.any
@@ -94,7 +94,7 @@
     private lateinit var connectionsRepo: FakeMobileConnectionsRepository
 
     @Mock private lateinit var telephonyManager: TelephonyManager
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: MobileInputLogger
     @Mock private lateinit var tableLogger: TableLogBuffer
 
     private val scope = CoroutineScope(IMMEDIATE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 8090205..09b7a66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -42,8 +42,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.util.mockito.any
@@ -86,7 +86,7 @@
     @Mock private lateinit var connectivityManager: ConnectivityManager
     @Mock private lateinit var subscriptionManager: SubscriptionManager
     @Mock private lateinit var telephonyManager: TelephonyManager
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: MobileInputLogger
     @Mock private lateinit var summaryLogger: TableLogBuffer
     @Mock private lateinit var logBufferFactory: TableLogBufferFactory
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index bbca001..c51dbf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -85,7 +85,6 @@
             MobileIconsInteractorImpl(
                 connectionsRepository,
                 carrierConfigTracker,
-                logger = mock(),
                 tableLogger = mock(),
                 connectivityRepository,
                 userSetupRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt
new file mode 100644
index 0000000..86529dc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.statusbar.pipeline.mobile.shared
+
+import android.net.Network
+import android.net.NetworkCapabilities
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.plugins.log.LogcatEchoTracker
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import org.junit.Test
+import org.mockito.Mockito
+import org.mockito.Mockito.mock
+
+@SmallTest
+class MobileInputLoggerTest : SysuiTestCase() {
+    private val buffer =
+        LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java)).create("buffer", 10)
+    private val logger = MobileInputLogger(buffer)
+
+    @Test
+    fun testLogNetworkCapsChange_bufferHasInfo() {
+        logger.logOnCapabilitiesChanged(NET_1, NET_1_CAPS, isDefaultNetworkCallback = true)
+
+        val stringWriter = StringWriter()
+        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
+        val actualString = stringWriter.toString()
+
+        val expectedNetId = NET_1_ID.toString()
+        val expectedCaps = NET_1_CAPS.toString()
+
+        assertThat(actualString).contains("true")
+        assertThat(actualString).contains(expectedNetId)
+        assertThat(actualString).contains(expectedCaps)
+    }
+
+    @Test
+    fun testLogOnLost_bufferHasNetIdOfLostNetwork() {
+        logger.logOnLost(NET_1)
+
+        val stringWriter = StringWriter()
+        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
+        val actualString = stringWriter.toString()
+
+        val expectedNetId = NET_1_ID.toString()
+
+        assertThat(actualString).contains(expectedNetId)
+    }
+
+    companion object {
+        private const val NET_1_ID = 100
+        private val NET_1 =
+            com.android.systemui.util.mockito.mock<Network>().also {
+                Mockito.`when`(it.getNetId()).thenReturn(NET_1_ID)
+            }
+        private val NET_1_CAPS =
+            NetworkCapabilities.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+                .build()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index d9268a2..4628f84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
@@ -51,7 +50,6 @@
 
     private lateinit var airplaneModeInteractor: AirplaneModeInteractor
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var constants: ConnectivityConstants
 
     private val testDispatcher = UnconfinedTestDispatcher()
@@ -77,7 +75,6 @@
                 subscriptionIdsFlow,
                 interactor,
                 airplaneModeInteractor,
-                logger,
                 constants,
                 testScope.backgroundScope,
                 statusBarPipelineFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
deleted file mode 100644
index 3dccbbf..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.pipeline.shared
-
-import android.net.Network
-import android.net.NetworkCapabilities
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
-import com.google.common.truth.Truth.assertThat
-import java.io.PrintWriter
-import java.io.StringWriter
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.runBlocking
-import org.junit.Test
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-
-@SmallTest
-class ConnectivityPipelineLoggerTest : SysuiTestCase() {
-    private val buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
-        .create("buffer", 10)
-    private val logger = ConnectivityPipelineLogger(buffer)
-
-    @Test
-    fun testLogNetworkCapsChange_bufferHasInfo() {
-        logger.logOnCapabilitiesChanged(NET_1, NET_1_CAPS, isDefaultNetworkCallback = true)
-
-        val stringWriter = StringWriter()
-        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
-        val actualString = stringWriter.toString()
-
-        val expectedNetId = NET_1_ID.toString()
-        val expectedCaps = NET_1_CAPS.toString()
-
-        assertThat(actualString).contains("true")
-        assertThat(actualString).contains(expectedNetId)
-        assertThat(actualString).contains(expectedCaps)
-    }
-
-    @Test
-    fun testLogOnLost_bufferHasNetIdOfLostNetwork() {
-        logger.logOnLost(NET_1)
-
-        val stringWriter = StringWriter()
-        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
-        val actualString = stringWriter.toString()
-
-        val expectedNetId = NET_1_ID.toString()
-
-        assertThat(actualString).contains(expectedNetId)
-    }
-
-    @Test
-    fun logOutputChange_printsValuesAndNulls() = runBlocking(IMMEDIATE) {
-        val flow: Flow<Int?> = flowOf(1, null, 3)
-
-        val job = flow
-            .logOutputChange(logger, "testInts")
-            .launchIn(this)
-
-        val stringWriter = StringWriter()
-        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
-        val actualString = stringWriter.toString()
-
-        assertThat(actualString).contains("1")
-        assertThat(actualString).contains("null")
-        assertThat(actualString).contains("3")
-
-        job.cancel()
-    }
-
-    @Test
-    fun logInputChange_unit_printsInputName() = runBlocking(IMMEDIATE) {
-        val flow: Flow<Unit> = flowOf(Unit, Unit)
-
-        val job = flow
-            .logInputChange(logger, "testInputs")
-            .launchIn(this)
-
-        val stringWriter = StringWriter()
-        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
-        val actualString = stringWriter.toString()
-
-        assertThat(actualString).contains("testInputs")
-
-        job.cancel()
-    }
-
-    @Test
-    fun logInputChange_any_printsValuesAndNulls() = runBlocking(IMMEDIATE) {
-        val flow: Flow<Any?> = flowOf(null, 2, "threeString")
-
-        val job = flow
-            .logInputChange(logger, "testInputs")
-            .launchIn(this)
-
-        val stringWriter = StringWriter()
-        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
-        val actualString = stringWriter.toString()
-
-        assertThat(actualString).contains("null")
-        assertThat(actualString).contains("2")
-        assertThat(actualString).contains("threeString")
-
-        job.cancel()
-    }
-
-    companion object {
-        private const val NET_1_ID = 100
-        private val NET_1 = com.android.systemui.util.mockito.mock<Network>().also {
-            Mockito.`when`(it.getNetId()).thenReturn(NET_1_ID)
-        }
-        private val NET_1_CAPS = NetworkCapabilities.Builder()
-            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-            .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
-            .build()
-        private val IMMEDIATE = Dispatchers.Main.immediate
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
index 6dbee2f..496f090 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
@@ -19,7 +19,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityInputLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE
@@ -52,7 +52,7 @@
 
     @Mock private lateinit var connectivitySlots: ConnectivitySlots
     @Mock private lateinit var dumpManager: DumpManager
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: ConnectivityInputLogger
     private lateinit var scope: CoroutineScope
     @Mock private lateinit var tunerService: TunerService
 
@@ -61,14 +61,15 @@
         MockitoAnnotations.initMocks(this)
         scope = CoroutineScope(IMMEDIATE)
 
-        underTest = ConnectivityRepositoryImpl(
-            connectivitySlots,
-            context,
-            dumpManager,
-            logger,
-            scope,
-            tunerService,
-        )
+        underTest =
+            ConnectivityRepositoryImpl(
+                connectivitySlots,
+                context,
+                dumpManager,
+                logger,
+                scope,
+                tunerService,
+            )
     }
 
     @After
@@ -77,199 +78,179 @@
     }
 
     @Test
-    fun forceHiddenSlots_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
-        context.getOrCreateTestableResources().addOverride(
-            DEFAULT_HIDDEN_ICONS_RESOURCE,
-            arrayOf(SLOT_WIFI, SLOT_ETHERNET)
-        )
-        // Re-create our [ConnectivityRepositoryImpl], since it fetches
-        // config_statusBarIconsToExclude when it's first constructed
-        underTest = ConnectivityRepositoryImpl(
-            connectivitySlots,
-            context,
-            dumpManager,
-            logger,
-            scope,
-            tunerService,
-        )
+    fun forceHiddenSlots_initiallyGetsDefault() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
+            context
+                .getOrCreateTestableResources()
+                .addOverride(DEFAULT_HIDDEN_ICONS_RESOURCE, arrayOf(SLOT_WIFI, SLOT_ETHERNET))
+            // Re-create our [ConnectivityRepositoryImpl], since it fetches
+            // config_statusBarIconsToExclude when it's first constructed
+            underTest =
+                ConnectivityRepositoryImpl(
+                    connectivitySlots,
+                    context,
+                    dumpManager,
+                    logger,
+                    scope,
+                    tunerService,
+                )
 
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
+            assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_slotNamesAdded_flowHasSlots() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
+    fun forceHiddenSlots_slotNamesAdded_flowHasSlots() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
 
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
 
-        assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+            assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_wrongKey_doesNotUpdate() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
+    fun forceHiddenSlots_wrongKey_doesNotUpdate() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
 
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
 
-        // WHEN onTuningChanged with the wrong key
-        getTunable().onTuningChanged("wrongKey", SLOT_WIFI)
-        yield()
+            // WHEN onTuningChanged with the wrong key
+            getTunable().onTuningChanged("wrongKey", SLOT_WIFI)
+            yield()
 
-        // THEN we didn't update our value and still have the old one
-        assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+            // THEN we didn't update our value and still have the old one
+            assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_slotNamesAddedThenNull_flowHasDefault() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
-        context.getOrCreateTestableResources().addOverride(
-            DEFAULT_HIDDEN_ICONS_RESOURCE,
-            arrayOf(SLOT_WIFI, SLOT_ETHERNET)
-        )
-        // Re-create our [ConnectivityRepositoryImpl], since it fetches
-        // config_statusBarIconsToExclude when it's first constructed
-        underTest = ConnectivityRepositoryImpl(
-            connectivitySlots,
-            context,
-            dumpManager,
-            logger,
-            scope,
-            tunerService,
-        )
+    fun forceHiddenSlots_slotNamesAddedThenNull_flowHasDefault() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
+            context
+                .getOrCreateTestableResources()
+                .addOverride(DEFAULT_HIDDEN_ICONS_RESOURCE, arrayOf(SLOT_WIFI, SLOT_ETHERNET))
+            // Re-create our [ConnectivityRepositoryImpl], since it fetches
+            // config_statusBarIconsToExclude when it's first constructed
+            underTest =
+                ConnectivityRepositoryImpl(
+                    connectivitySlots,
+                    context,
+                    dumpManager,
+                    logger,
+                    scope,
+                    tunerService,
+                )
 
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        // First, update the slots
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
-        assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+            // First, update the slots
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+            assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
 
-        // WHEN we update to a null value
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null)
-        yield()
+            // WHEN we update to a null value
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null)
+            yield()
 
-        // THEN we go back to our default value
-        assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
+            // THEN we go back to our default value
+            assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() = runBlocking(IMMEDIATE) {
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+    fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() =
+        runBlocking(IMMEDIATE) {
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        whenever(connectivitySlots.getSlotFromName(SLOT_WIFI))
-            .thenReturn(ConnectivitySlot.WIFI)
-        whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
+            whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(ConnectivitySlot.WIFI)
+            whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
 
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE")
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE")
 
-        assertThat(latest).containsExactly(ConnectivitySlot.WIFI)
+            assertThat(latest).containsExactly(ConnectivitySlot.WIFI)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_someEmptySlotNames_flowHasValidSlotsOnly() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
+    fun forceHiddenSlots_someEmptySlotNames_flowHasValidSlotsOnly() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
 
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        // WHEN there's empty and blank slot names
-        getTunable().onTuningChanged(
-            HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE,  ,,$SLOT_WIFI"
-        )
+            // WHEN there's empty and blank slot names
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE,  ,,$SLOT_WIFI")
 
-        // THEN we skip that slot but still process the other ones
-        assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE)
+            // THEN we skip that slot but still process the other ones
+            assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() = runBlocking(IMMEDIATE) {
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+    fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() =
+        runBlocking(IMMEDIATE) {
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null)
-        whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null)
-        whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
+            whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null)
+            whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null)
+            whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
 
-        getTunable().onTuningChanged(
-            HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE,,$SLOT_WIFI,$SLOT_ETHERNET,,,"
-        )
+            getTunable()
+                .onTuningChanged(
+                    HIDDEN_ICONS_TUNABLE_KEY,
+                    "$SLOT_MOBILE,,$SLOT_WIFI,$SLOT_ETHERNET,,,"
+                )
 
-        assertThat(latest).isEmpty()
+            assertThat(latest).isEmpty()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_newSubscriberGetsCurrentValue() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
+    fun forceHiddenSlots_newSubscriberGetsCurrentValue() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
 
-        var latest1: Set<ConnectivitySlot>? = null
-        val job1 = underTest
-            .forceHiddenSlots
-            .onEach { latest1 = it }
-            .launchIn(this)
+            var latest1: Set<ConnectivitySlot>? = null
+            val job1 = underTest.forceHiddenSlots.onEach { latest1 = it }.launchIn(this)
 
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET")
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET")
 
-        assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
+            assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
 
-        // WHEN we add a second subscriber after having already emitted a value
-        var latest2: Set<ConnectivitySlot>? = null
-        val job2 = underTest
-            .forceHiddenSlots
-            .onEach { latest2 = it }
-            .launchIn(this)
+            // WHEN we add a second subscriber after having already emitted a value
+            var latest2: Set<ConnectivitySlot>? = null
+            val job2 = underTest.forceHiddenSlots.onEach { latest2 = it }.launchIn(this)
 
-        // THEN the second subscribe receives the already-emitted value
-        assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
+            // THEN the second subscribe receives the already-emitted value
+            assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
 
-        job1.cancel()
-        job2.cancel()
-    }
+            job1.cancel()
+            job2.cancel()
+        }
 
     private fun getTunable(): TunerService.Tunable {
         val callbackCaptor = argumentCaptor<TunerService.Tunable>()
@@ -280,10 +261,8 @@
     private fun setUpEthernetWifiMobileSlotNames() {
         whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET))
             .thenReturn(ConnectivitySlot.ETHERNET)
-        whenever(connectivitySlots.getSlotFromName(SLOT_WIFI))
-            .thenReturn(ConnectivitySlot.WIFI)
-        whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE))
-            .thenReturn(ConnectivitySlot.MOBILE)
+        whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(ConnectivitySlot.WIFI)
+        whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(ConnectivitySlot.MOBILE)
     }
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index 1085c2b..25678b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -23,11 +23,11 @@
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.kotlinArgumentCaptor
 import com.android.systemui.util.mockito.whenever
@@ -47,6 +47,7 @@
 import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @SmallTest
 class WifiRepositorySwitcherTest : SysuiTestCase() {
     private lateinit var underTest: WifiRepositorySwitcher
@@ -54,7 +55,7 @@
     private lateinit var demoImpl: DemoWifiRepository
 
     @Mock private lateinit var demoModeController: DemoModeController
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: WifiInputLogger
     @Mock private lateinit var tableLogger: TableLogBuffer
     @Mock private lateinit var connectivityManager: ConnectivityManager
     @Mock private lateinit var wifiManager: WifiManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index db791bb..c7b31bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -34,9 +34,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -72,7 +72,7 @@
     private lateinit var underTest: WifiRepositoryImpl
 
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: WifiInputLogger
     @Mock private lateinit var tableLogger: TableLogBuffer
     @Mock private lateinit var connectivityManager: ConnectivityManager
     @Mock private lateinit var wifiManager: WifiManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 60f564e..64810d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -36,7 +36,6 @@
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
@@ -63,7 +62,6 @@
     private lateinit var testableLooper: TestableLooper
 
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
     @Mock private lateinit var connectivityConstants: ConnectivityConstants
     @Mock private lateinit var wifiConstants: WifiConstants
@@ -92,7 +90,7 @@
                     airplaneModeRepository,
                     connectivityRepository,
                 ),
-                logger,
+                tableLogBuffer,
                 scope,
             )
         viewModel =
@@ -100,7 +98,6 @@
                     airplaneModeViewModel,
                     connectivityConstants,
                     context,
-                    logger,
                     tableLogBuffer,
                     interactor,
                     scope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index 648d7a5..12b1664 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -33,7 +33,6 @@
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
@@ -68,7 +67,6 @@
     private lateinit var underTest: WifiViewModel
 
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
     @Mock private lateinit var connectivityConstants: ConnectivityConstants
     @Mock private lateinit var wifiConstants: WifiConstants
@@ -94,7 +92,7 @@
                     airplaneModeRepository,
                     connectivityRepository,
                 ),
-                logger,
+                tableLogBuffer,
                 scope,
             )
     }
@@ -125,7 +123,6 @@
                     airplaneModeViewModel,
                     connectivityConstants,
                     context,
-                    logger,
                     tableLogBuffer,
                     interactor,
                     scope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 45ebb39..7a62cb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
@@ -59,7 +58,6 @@
     private lateinit var underTest: WifiViewModel
 
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
     @Mock private lateinit var connectivityConstants: ConnectivityConstants
     @Mock private lateinit var wifiConstants: WifiConstants
@@ -85,7 +83,7 @@
                     airplaneModeRepository,
                     connectivityRepository,
                 ),
-                logger,
+                tableLogBuffer,
                 scope,
             )
 
@@ -478,7 +476,6 @@
                 airplaneModeViewModel,
                 connectivityConstants,
                 context,
-                logger,
                 tableLogBuffer,
                 interactor,
                 scope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index 3d75967..a87e61a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
 import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.shade.NotificationPanelViewController
@@ -112,7 +113,8 @@
             KeyguardInteractor(
                 repository = keyguardRepository,
                 commandQueue = commandQueue,
-                featureFlags = featureFlags
+                featureFlags = featureFlags,
+                bouncerRepository = FakeKeyguardBouncerRepository(),
             )
 
         // Needs to be run on the main thread
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 22d05bf..f0a53ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
@@ -145,6 +146,7 @@
                         repository = keyguardRepository,
                         commandQueue = commandQueue,
                         featureFlags = featureFlags,
+                        bouncerRepository = FakeKeyguardBouncerRepository(),
                     ),
                 manager = manager,
                 headlessSystemUserMode = headlessSystemUserMode,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 2f05e65..bc0881c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
@@ -251,6 +252,7 @@
                             repository = keyguardRepository,
                             commandQueue = commandQueue,
                             featureFlags = featureFlags,
+                            bouncerRepository = FakeKeyguardBouncerRepository(),
                         ),
                     featureFlags = featureFlags,
                     manager = manager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 9268904..c8b0496 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
@@ -152,7 +153,8 @@
                                 KeyguardInteractor(
                                     repository = keyguardRepository,
                                     commandQueue = commandQueue,
-                                    featureFlags = featureFlags
+                                    featureFlags = featureFlags,
+                                    bouncerRepository = FakeKeyguardBouncerRepository(),
                                 ),
                             featureFlags = featureFlags,
                             manager = manager,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
index d0383e9..3374219 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
@@ -57,10 +57,10 @@
     override val bouncerPromptReason = 0
     override val bouncerErrorMessage: CharSequence? = null
     private val _isAlternateBouncerVisible = MutableStateFlow(false)
-    override val isAlternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
+    override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
     override var lastAlternateBouncerVisibleTime: Long = 0L
     private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false)
-    override val isAlternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow()
+    override val alternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow()
 
     override fun setPrimaryScrimmed(isScrimmed: Boolean) {
         _primaryBouncerScrimmed.value = isScrimmed
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 065fe89..1a371c7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -84,9 +84,6 @@
 
     private val _isUdfpsSupported = MutableStateFlow(false)
 
-    private val _isBouncerShowing = MutableStateFlow(false)
-    override val isBouncerShowing: Flow<Boolean> = _isBouncerShowing
-
     private val _isKeyguardGoingAway = MutableStateFlow(false)
     override val isKeyguardGoingAway: Flow<Boolean> = _isKeyguardGoingAway
 
@@ -153,10 +150,6 @@
         _wakefulnessModel.value = model
     }
 
-    fun setBouncerShowing(isShowing: Boolean) {
-        _isBouncerShowing.value = isShowing
-    }
-
     fun setBiometricUnlockState(state: BiometricUnlockModel) {
         _biometricUnlockState.tryEmit(state)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
index 95b62a1..bdf1aff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
@@ -49,7 +49,7 @@
     }
 
     @Override
-    public boolean isBouncerShowing() {
+    public boolean isPrimaryBouncerShowing() {
         return false;
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 72c5598..328b971 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3819,20 +3819,30 @@
                     + "proxy-ed");
         }
 
-        mProxyManager.registerProxy(client, displayId, mContext,
-                sIdCounter++, mMainHandler, mSecurityPolicy, this, getTraceManager(),
-                mWindowManagerService, mA11yWindowManager);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mProxyManager.registerProxy(client, displayId, mContext,
+                    sIdCounter++, mMainHandler, mSecurityPolicy, this, getTraceManager(),
+                    mWindowManagerService, mA11yWindowManager);
 
-        synchronized (mLock) {
-            notifyClearAccessibilityCacheLocked();
+            synchronized (mLock) {
+                notifyClearAccessibilityCacheLocked();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
         return true;
     }
 
     @Override
-    public boolean unregisterProxyForDisplay(int displayId) throws RemoteException {
+    public boolean unregisterProxyForDisplay(int displayId) {
         mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
-        return mProxyManager.unregisterProxy(displayId);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mProxyManager.unregisterProxy(displayId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     boolean isDisplayProxyed(int displayId) {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index dc475f6..3a7aa85 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -158,19 +158,19 @@
     /**
      * The user that the backup is activated by default for.
      *
-     * If there is a {@link UserManager#getMainUser()}, this will be that user. If not, it will be
-     * {@link UserHandle#USER_SYSTEM}.
+     * <p>If there is a {@link UserManager#getMainUser()}, this will be that user. If not, it will
+     * be {@link UserHandle#USER_SYSTEM}.
+     *
+     * <p>Note: on the first ever boot of a new device, this might change once the first user is
+     * unlocked. See {@link #updateDefaultBackupUserIdIfNeeded()}.
      *
      * @see #isBackupActivatedForUser(int)
      */
-    @UserIdInt private final int mDefaultBackupUserId;
+    @UserIdInt private int mDefaultBackupUserId;
+
+    private boolean mHasFirstUserUnlockedSinceBoot = false;
 
     public BackupManagerService(Context context) {
-        this(context, new SparseArray<>());
-    }
-
-    @VisibleForTesting
-    BackupManagerService(Context context, SparseArray<UserBackupManagerService> userServices) {
         mContext = context;
         mGlobalDisable = isBackupDisabled();
         HandlerThread handlerThread =
@@ -178,7 +178,7 @@
         handlerThread.start();
         mHandler = new Handler(handlerThread.getLooper());
         mUserManager = UserManager.get(context);
-        mUserServices = userServices;
+        mUserServices = new SparseArray<>();
         Set<ComponentName> transportWhitelist =
                 SystemConfig.getInstance().getBackupTransportWhitelist();
         mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist;
@@ -186,6 +186,9 @@
                 mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
         UserHandle mainUser = getUserManager().getMainUser();
         mDefaultBackupUserId = mainUser == null ? UserHandle.USER_SYSTEM : mainUser.getIdentifier();
+        if (DEBUG) {
+            Slog.d(TAG, "Default backup user id = " + mDefaultBackupUserId);
+        }
     }
 
     // TODO: Remove this when we implement DI by injecting in the construtor.
@@ -361,17 +364,6 @@
     }
 
     /**
-     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
-     * Starts the backup service for this user if backup is active for this user. Offloads work onto
-     * the handler thread {@link #mHandlerThread} to keep unlock time low since backup is not
-     * essential for device functioning.
-     */
-    @VisibleForTesting
-    void onUnlockUser(int userId) {
-        postToHandler(() -> startServiceForUser(userId));
-    }
-
-    /**
      * Starts the backup service for user {@code userId} by creating a new instance of {@link
      * UserBackupManagerService} and registering it with this service.
      */
@@ -1687,7 +1679,15 @@
 
         @Override
         public void onUserUnlocking(@NonNull TargetUser user) {
-            sInstance.onUnlockUser(user.getUserIdentifier());
+            // Starts the backup service for this user if backup is active for this user. Offloads
+            // work onto the handler thread {@link #mHandlerThread} to keep unlock time low since
+            // backup is not essential for device functioning.
+            sInstance.postToHandler(
+                    () -> {
+                        sInstance.updateDefaultBackupUserIdIfNeeded();
+                        sInstance.startServiceForUser(user.getUserIdentifier());
+                        sInstance.mHasFirstUserUnlockedSinceBoot = true;
+                    });
         }
 
         @Override
@@ -1700,4 +1700,42 @@
             publishBinderService(name, service);
         }
     }
+
+    /**
+     * On the first ever boot of a new device, the 'main' user might not exist for a short period of
+     * time and be created after {@link BackupManagerService} is created. In this case the {@link
+     * #mDefaultBackupUserId} will be the system user initially, but we need to change it to the
+     * newly created {@link UserManager#getMainUser()} later.
+     *
+     * <p>{@link Lifecycle#onUserUnlocking(SystemService.TargetUser)} (for any user) is the earliest
+     * point where we know that a main user (if there is going to be one) is created.
+     */
+    private void updateDefaultBackupUserIdIfNeeded() {
+        // The default user can only change before any user unlocks since boot, and it will only
+        // change from the system user to a non-system user.
+        if (mHasFirstUserUnlockedSinceBoot || mDefaultBackupUserId != UserHandle.USER_SYSTEM) {
+            return;
+        }
+
+        UserHandle mainUser = getUserManager().getMainUser();
+        if (mainUser == null) {
+            return;
+        }
+
+        if (mDefaultBackupUserId != mainUser.getIdentifier()) {
+            int oldDefaultBackupUserId = mDefaultBackupUserId;
+            mDefaultBackupUserId = mainUser.getIdentifier();
+            // We don't expect the service to be started for the old default user but we attempt to
+            // stop its service to be safe.
+            if (!isBackupActivatedForUser(oldDefaultBackupUserId)) {
+                stopServiceForUser(oldDefaultBackupUserId);
+            }
+            Slog.i(
+                    TAG,
+                    "Default backup user changed from "
+                            + oldDefaultBackupUserId
+                            + " to "
+                            + mDefaultBackupUserId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a66e598..57a0713 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -416,6 +416,11 @@
             LinkCapacityEstimate.INVALID, LinkCapacityEstimate.INVALID)));
     private List<List<LinkCapacityEstimate>> mLinkCapacityEstimateLists;
 
+    private int[] mECBMReason;
+    private boolean[] mECBMStarted;
+    private int[] mSCBMReason;
+    private boolean[] mSCBMStarted;
+
     /**
      * Per-phone map of precise data connection state. The key of the map is the pair of transport
      * type and APN setting. This is the cache to prevent redundant callbacks to the listeners.
@@ -556,7 +561,8 @@
         return events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
                 || events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
                 || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
-                || events.contains(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
+                || events.contains(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED);
     }
 
     private static final int MSG_USER_SWITCHED = 1;
@@ -707,7 +713,10 @@
             mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones);
             mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones);
             mAllowedNetworkTypeValue = copyOf(mAllowedNetworkTypeValue, mNumPhones);
-
+            mECBMReason = copyOf(mECBMReason, mNumPhones);
+            mECBMStarted = copyOf(mECBMStarted, mNumPhones);
+            mSCBMReason = copyOf(mSCBMReason, mNumPhones);
+            mSCBMStarted = copyOf(mSCBMStarted, mNumPhones);
             // ds -> ss switch.
             if (mNumPhones < oldNumPhones) {
                 cutListToSize(mCellInfo, mNumPhones);
@@ -762,6 +771,10 @@
                 mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST);
                 mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
                 mCarrierServiceStates.add(i, new Pair<>(null, Process.INVALID_UID));
+                mECBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN;
+                mECBMStarted[i] = false;
+                mSCBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN;
+                mSCBMStarted[i] = false;
             }
         }
     }
@@ -831,6 +844,10 @@
         mLinkCapacityEstimateLists = new ArrayList<>();
         mCarrierPrivilegeStates = new ArrayList<>();
         mCarrierServiceStates = new ArrayList<>();
+        mECBMReason = new int[numPhones];
+        mECBMStarted = new boolean[numPhones];
+        mSCBMReason = new int[numPhones];
+        mSCBMStarted = new boolean[numPhones];
 
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
@@ -870,6 +887,10 @@
             mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST);
             mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
             mCarrierServiceStates.add(i, new Pair<>(null, Process.INVALID_UID));
+            mECBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN;
+            mECBMStarted[i] = false;
+            mSCBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN;
+            mSCBMStarted[i] = false;
         }
 
         mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1441,6 +1462,31 @@
                         }
                     }
                 }
+                if (events.contains(TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)) {
+                    try {
+                        boolean ecbmStarted = mECBMStarted[r.phoneId];
+                        if (ecbmStarted) {
+                            r.callback.onCallBackModeStarted(
+                                    TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL);
+                        } else {
+                            r.callback.onCallBackModeStopped(
+                                    TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL,
+                                    mECBMReason[r.phoneId]);
+                        }
+
+                        boolean scbmStarted = mSCBMStarted[r.phoneId];
+                        if (scbmStarted) {
+                            r.callback.onCallBackModeStarted(
+                                    TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS);
+                        } else {
+                            r.callback.onCallBackModeStopped(
+                                    TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS,
+                                    mSCBMReason[r.phoneId]);
+                        }
+                    } catch (RemoteException ex) {
+                        remove(r.binder);
+                    }
+                }
             }
         }
     }
@@ -3258,6 +3304,72 @@
         }
     }
 
+    @Override
+    public void notifyCallbackModeStarted(int phoneId, int subId, int type) {
+        if (!checkNotifyPermission("notifyCallbackModeStarted()")) {
+            return;
+        }
+        if (VDBG) {
+            log("notifyCallbackModeStarted: phoneId=" + phoneId + ", subId=" + subId
+                    + ", type=" + type);
+        }
+        synchronized (mRecords) {
+            if (validatePhoneId(phoneId)) {
+                if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL) {
+                    mECBMStarted[phoneId] = true;
+                } else if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS) {
+                    mSCBMStarted[phoneId] = true;
+                }
+            }
+            for (Record r : mRecords) {
+                // Send to all listeners regardless of subscription
+                if (r.matchTelephonyCallbackEvent(
+                        TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)) {
+                    try {
+                        r.callback.onCallBackModeStarted(type);
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+        }
+        handleRemoveListLocked();
+    }
+
+    @Override
+    public void notifyCallbackModeStopped(int phoneId, int subId, int type, int reason) {
+        if (!checkNotifyPermission("notifyCallbackModeStopped()")) {
+            return;
+        }
+        if (VDBG) {
+            log("notifyCallbackModeStopped: phoneId=" + phoneId + ", subId=" + subId
+                    + ", type=" + type + ", reason=" + reason);
+        }
+        synchronized (mRecords) {
+            if (validatePhoneId(phoneId)) {
+                if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL) {
+                    mECBMStarted[phoneId] = false;
+                    mECBMReason[phoneId] = reason;
+                } else if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS) {
+                    mSCBMStarted[phoneId] = false;
+                    mSCBMReason[phoneId] = reason;
+                }
+            }
+            for (Record r : mRecords) {
+                // Send to all listeners regardless of subscription
+                if (r.matchTelephonyCallbackEvent(
+                        TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)) {
+                    try {
+                        r.callback.onCallBackModeStopped(type, reason);
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+        }
+        handleRemoveListLocked();
+    }
+
     @NeverCompile // Avoid size overhead of debugging code.
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -3307,6 +3419,11 @@
                 pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
                 pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs.get(i));
                 pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i));
+                pw.println("mECBMReason=" + mECBMReason[i]);
+                pw.println("mECBMStarted=" + mECBMStarted[i]);
+                pw.println("mSCBMReason=" + mSCBMReason[i]);
+                pw.println("mSCBMStarted=" + mSCBMStarted[i]);
+
                 // We need to obfuscate package names, and primitive arrays' native toString is ugly
                 Pair<List<String>, int[]> carrierPrivilegeState = mCarrierPrivilegeStates.get(i);
                 pw.println(
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index f5ca8aa..82e6e30 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1882,6 +1882,10 @@
                     ? BrightnessEvent.FLAG_USER_SET : 0));
             Slog.i(mTag, newEvent.toString(/* includeTime= */ false));
 
+            // Log all events which are not temporary
+            if (newEvent.getReason().getReason() != BrightnessReason.REASON_TEMPORARY) {
+                logBrightnessEvent(newEvent, unthrottledBrightnessState);
+            }
             if (userSetBrightnessChanged) {
                 logManualBrightnessEvent(newEvent);
             }
@@ -2976,6 +2980,154 @@
         }
     }
 
+    // Return bucket index of range_[left]_[right] where
+    // left <= nits < right
+    private int nitsToRangeIndex(float nits) {
+        float[] boundaries = {
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80,
+            90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1200,
+            1400, 1600, 1800, 2000, 2250, 2500, 2750, 3000};
+        int[] rangeIndex = {
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_UNKNOWN,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_0_1,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1_2,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_2_3,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_3_4,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_4_5,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_5_6,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_6_7,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_7_8,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_8_9,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_9_10,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_10_20,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_20_30,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_30_40,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_40_50,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_50_60,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_60_70,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_70_80,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_80_90,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_90_100,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_100_200,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_200_300,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_300_400,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_400_500,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_500_600,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_600_700,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_700_800,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_800_900,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_900_1000,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1000_1200,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1200_1400,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1400_1600,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1600_1800,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1800_2000,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_2000_2250,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_2250_2500,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_2500_2750,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_2750_3000,
+        };
+        for (int i = 0; i < boundaries.length; i++) {
+            if (nits < boundaries[i]) {
+                return rangeIndex[i];
+            }
+        }
+        return FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_3000_INF;
+    }
+
+    private int convertBrightnessReasonToStatsEnum(int brightnessReason) {
+        switch(brightnessReason) {
+            case BrightnessReason.REASON_UNKNOWN:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_UNKNOWN;
+            case BrightnessReason.REASON_MANUAL:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_MANUAL;
+            case BrightnessReason.REASON_DOZE:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_DOZE;
+            case BrightnessReason.REASON_DOZE_DEFAULT:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_DOZE_DEFAULT;
+            case BrightnessReason.REASON_AUTOMATIC:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_AUTOMATIC;
+            case BrightnessReason.REASON_SCREEN_OFF:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_SCREEN_OFF;
+            case BrightnessReason.REASON_OVERRIDE:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_OVERRIDE;
+            case BrightnessReason.REASON_TEMPORARY:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_TEMPORARY;
+            case BrightnessReason.REASON_BOOST:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_BOOST;
+            case BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_SCREEN_OFF_BRIGHTNESS_SENSOR;
+            case BrightnessReason.REASON_FOLLOWER:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_FOLLOWER;
+        }
+        return FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_UNKNOWN;
+    }
+
+    private int convertHbmModeToStatsEnum(int mode) {
+        switch(mode) {
+            case BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__HBM_MODE__HIGH_BRIGHTNESS_MODE_OFF;
+            case BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__HBM_MODE__HIGH_BRIGHTNESS_MODE_SUNLIGHT;
+            case BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__HBM_MODE__HIGH_BRIGHTNESS_MODE_HDR;
+        }
+        return FrameworkStatsLog
+            .SCREEN_BRIGHTNESS_CHANGED_V2__HBM_MODE__HIGH_BRIGHTNESS_MODE_OFF;
+    }
+
+    // unmodifiedBrightness: the brightness value that has not been
+    // modified by any modifiers(dimming/throttling/low-power-mode)
+    private void logBrightnessEvent(BrightnessEvent event, float unmodifiedBrightness) {
+        int modifier = event.getReason().getModifier();
+        // It's easier to check if the brightness is at maximum level using the brightness
+        // value untouched by any modifiers
+        boolean brightnessIsMax = unmodifiedBrightness == event.getHbmMax();
+        float brightnessInNits = convertToNits(event.getBrightness());
+        float lowPowerModeFactor = event.getPowerFactor();
+        int rbcStrength  = event.getRbcStrength();
+        float hbmMaxNits = convertToNits(event.getHbmMax());
+        float thermalCapNits = convertToNits(event.getThermalMax());
+
+        if (mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
+                && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
+                    .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL) {
+            FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2,
+                    event.getPhysicalDisplayId().hashCode(),
+                    brightnessInNits,
+                    convertToNits(unmodifiedBrightness),
+                    nitsToRangeIndex(brightnessInNits),
+                    brightnessIsMax,
+                    (event.getFlags() & BrightnessEvent.FLAG_USER_SET) > 0,
+                    convertBrightnessReasonToStatsEnum(event.getReason().getReason()),
+                    convertHbmModeToStatsEnum(event.getHbmMode()),
+                    (modifier & BrightnessReason.MODIFIER_LOW_POWER) > 0,
+                    (modifier & BrightnessReason.MODIFIER_THROTTLED) > 0,
+                    event.isRbcEnabled(),
+                    event.getLux(),
+                    event.wasShortTermModelActive(),
+                    lowPowerModeFactor,
+                    rbcStrength,
+                    hbmMaxNits,
+                    thermalCapNits,
+                    event.isAutomaticBrightnessEnabled());
+        }
+    }
+
     private void logManualBrightnessEvent(BrightnessEvent event) {
         float appliedLowPowerMode = event.isLowPowerModeSet() ? event.getPowerFactor() : -1f;
         int appliedRbcStrength  = event.isRbcEnabled() ? event.getRbcStrength() : -1;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 5667ddf..39bc09e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -1595,6 +1595,10 @@
                     ? BrightnessEvent.FLAG_USER_SET : 0));
             Slog.i(mTag, newEvent.toString(/* includeTime= */ false));
 
+            // Log all events which are not temporary
+            if (newEvent.getReason().getReason() != BrightnessReason.REASON_TEMPORARY) {
+                logBrightnessEvent(newEvent, unthrottledBrightnessState);
+            }
             if (userSetBrightnessChanged) {
                 logManualBrightnessEvent(newEvent);
             }
@@ -2512,6 +2516,154 @@
         }
     }
 
+    // Return bucket index of range_[left]_[right] where
+    // left <= nits < right
+    private int nitsToRangeIndex(float nits) {
+        float[] boundaries = {
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80,
+            90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1200,
+            1400, 1600, 1800, 2000, 2250, 2500, 2750, 3000};
+        int[] rangeIndex = {
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_UNKNOWN,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_0_1,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1_2,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_2_3,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_3_4,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_4_5,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_5_6,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_6_7,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_7_8,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_8_9,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_9_10,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_10_20,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_20_30,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_30_40,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_40_50,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_50_60,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_60_70,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_70_80,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_80_90,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_90_100,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_100_200,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_200_300,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_300_400,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_400_500,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_500_600,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_600_700,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_700_800,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_800_900,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_900_1000,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1000_1200,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1200_1400,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1400_1600,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1600_1800,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_1800_2000,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_2000_2250,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_2250_2500,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_2500_2750,
+            FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_2750_3000,
+        };
+        for (int i = 0; i < boundaries.length; i++) {
+            if (nits < boundaries[i]) {
+                return rangeIndex[i];
+            }
+        }
+        return FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__BUCKET_INDEX__RANGE_3000_INF;
+    }
+
+    private int convertBrightnessReasonToStatsEnum(int brightnessReason) {
+        switch(brightnessReason) {
+            case BrightnessReason.REASON_UNKNOWN:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_UNKNOWN;
+            case BrightnessReason.REASON_MANUAL:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_MANUAL;
+            case BrightnessReason.REASON_DOZE:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_DOZE;
+            case BrightnessReason.REASON_DOZE_DEFAULT:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_DOZE_DEFAULT;
+            case BrightnessReason.REASON_AUTOMATIC:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_AUTOMATIC;
+            case BrightnessReason.REASON_SCREEN_OFF:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_SCREEN_OFF;
+            case BrightnessReason.REASON_OVERRIDE:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_OVERRIDE;
+            case BrightnessReason.REASON_TEMPORARY:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_TEMPORARY;
+            case BrightnessReason.REASON_BOOST:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_BOOST;
+            case BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_SCREEN_OFF_BRIGHTNESS_SENSOR;
+            case BrightnessReason.REASON_FOLLOWER:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_FOLLOWER;
+        }
+        return FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2__REASON__REASON_UNKNOWN;
+    }
+
+    private int convertHbmModeToStatsEnum(int mode) {
+        switch(mode) {
+            case BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__HBM_MODE__HIGH_BRIGHTNESS_MODE_OFF;
+            case BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__HBM_MODE__HIGH_BRIGHTNESS_MODE_SUNLIGHT;
+            case BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR:
+                return FrameworkStatsLog
+                    .SCREEN_BRIGHTNESS_CHANGED_V2__HBM_MODE__HIGH_BRIGHTNESS_MODE_HDR;
+        }
+        return FrameworkStatsLog
+            .SCREEN_BRIGHTNESS_CHANGED_V2__HBM_MODE__HIGH_BRIGHTNESS_MODE_OFF;
+    }
+
+    // unmodifiedBrightness: the brightness value that has not been
+    // modified by any modifiers(dimming/throttling/low-power-mode)
+    private void logBrightnessEvent(BrightnessEvent event, float unmodifiedBrightness) {
+        int modifier = event.getReason().getModifier();
+        // It's easier to check if the brightness is at maximum level using the brightness
+        // value untouched by any modifiers
+        boolean brightnessIsMax = unmodifiedBrightness == event.getHbmMax();
+        float brightnessInNits = convertToNits(event.getBrightness());
+        float lowPowerModeFactor = event.getPowerFactor();
+        int rbcStrength  = event.getRbcStrength();
+        float hbmMaxNits = convertToNits(event.getHbmMax());
+        float thermalCapNits = convertToNits(event.getThermalMax());
+
+        if (mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
+                && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
+                    .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL) {
+            FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED_V2,
+                    event.getPhysicalDisplayId().hashCode(),
+                    brightnessInNits,
+                    convertToNits(unmodifiedBrightness),
+                    nitsToRangeIndex(brightnessInNits),
+                    brightnessIsMax,
+                    (event.getFlags() & BrightnessEvent.FLAG_USER_SET) > 0,
+                    convertBrightnessReasonToStatsEnum(event.getReason().getReason()),
+                    convertHbmModeToStatsEnum(event.getHbmMode()),
+                    (modifier & BrightnessReason.MODIFIER_LOW_POWER) > 0,
+                    (modifier & BrightnessReason.MODIFIER_THROTTLED) > 0,
+                    event.isRbcEnabled(),
+                    event.getLux(),
+                    event.wasShortTermModelActive(),
+                    lowPowerModeFactor,
+                    rbcStrength,
+                    hbmMaxNits,
+                    thermalCapNits,
+                    event.isAutomaticBrightnessEnabled());
+        }
+    }
+
     private final class DisplayControllerHandler extends Handler {
         DisplayControllerHandler(Looper looper) {
             super(looper, null, true /*async*/);
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 1823a39..c1171fa 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -18,7 +18,6 @@
 
 import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
 import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
-import static android.safetylabel.SafetyLabelConstants.PERMISSION_RATIONALE_ENABLED;
 import static android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED;
 
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
@@ -392,8 +391,7 @@
     /** Returns whether the Safety Label Change notification, a privacy feature, is enabled. */
     public static boolean isPrivacySafetyLabelChangeNotificationsEnabled() {
         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, false) && DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_PRIVACY, PERMISSION_RATIONALE_ENABLED, false);
+                SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, false);
     }
 
     @NonNull
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 25f9ccd..fb6ae8b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -66,6 +66,8 @@
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -124,6 +126,7 @@
 import android.os.RemoteException;
 import android.os.RevocableFileDescriptor;
 import android.os.SELinux;
+import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.incremental.IStorageHealthListener;
@@ -146,6 +149,7 @@
 import android.util.EventLog;
 import android.util.ExceptionUtils;
 import android.util.IntArray;
+import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -154,6 +158,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.content.InstallLocationUtils;
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.messages.nano.SystemMessageProto;
@@ -297,8 +302,18 @@
     private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000;
 
     /**
+     * If an app being installed targets {@link Build.VERSION_CODES#S API 31} and above, the app
+     * can be installed without user action.
+     * See {@link PackageInstaller.SessionParams#setRequireUserAction} for other conditions required
+     * to be satisfied for a silent install.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long SILENT_INSTALL_ALLOWED = 265131695L;
+
+    /**
      * The default value of {@link #mValidatedTargetSdk} is {@link Integer#MAX_VALUE}. If {@link
-     * #mValidatedTargetSdk} is compared with {@link Build.VERSION_CODES#R} before getting the
+     * #mValidatedTargetSdk} is compared with {@link Build.VERSION_CODES#S} before getting the
      * target sdk version from a validated apk in {@link #validateApkInstallLocked()}, the compared
      * result will not trigger any user action in
      * {@link #checkUserActionRequirement(PackageInstallerSession, IntentSender)}.
@@ -2353,13 +2368,7 @@
         }
 
         if (!session.isApexSession() && userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
-            final int validatedTargetSdk;
-            synchronized (session.mLock) {
-                validatedTargetSdk = session.mValidatedTargetSdk;
-            }
-
-            if (validatedTargetSdk != INVALID_TARGET_SDK_VERSION
-                    && validatedTargetSdk < Build.VERSION_CODES.R) {
+            if (!isTargetSdkConditionSatisfied(session)) {
                 session.sendPendingUserActionIntent(target);
                 return true;
             }
@@ -2380,6 +2389,39 @@
         return false;
     }
 
+    /**
+     * Checks if the app being installed has a targetSdk more than the minimum required for a
+     * silent install. See {@link SessionParams#setRequireUserAction(int)} for details about the
+     * targetSdk requirement.
+     * @param session Current install session
+     * @return true if the targetSdk of the app being installed is more than the minimum required,
+     *          resulting in a silent install, false otherwise.
+     */
+    private static boolean isTargetSdkConditionSatisfied(PackageInstallerSession session) {
+        final int validatedTargetSdk;
+        final String packageName;
+        synchronized (session.mLock) {
+            validatedTargetSdk = session.mValidatedTargetSdk;
+            packageName = session.mPackageName;
+        }
+
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = packageName;
+        appInfo.targetSdkVersion = validatedTargetSdk;
+
+        IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+                ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        try {
+            // Using manually constructed AppInfo to check if a change is enabled may not work
+            // in the future.
+            return validatedTargetSdk != INVALID_TARGET_SDK_VERSION
+                    && platformCompat.isChangeEnabled(SILENT_INSTALL_ALLOWED, appInfo);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e);
+            return false;
+        }
+    }
+
     private static @UserActionReason int userActionRequirementToReason(
             @UserActionRequirement int requirement) {
         switch (requirement) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 89a9eb0..78761bd 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -3305,7 +3305,7 @@
         if (Objects.equals(packageName, PLATFORM_PACKAGE_NAME)) {
             return true;
         }
-        if (!packageSetting.isPrivileged()) {
+        if (!(packageSetting.isSystem() && packageSetting.isPrivileged())) {
             return true;
         }
         if (!mPrivilegedPermissionAllowlistSourcePackageNames
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 8ceac79..de7dc3b 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -1394,6 +1394,22 @@
         return false
     }
 
+    private fun addAllowlistedRestrictedPermissionsUnchecked(
+        androidPackage: AndroidPackage,
+        appId: Int,
+        permissionNames: List<String>,
+        userId: Int
+    ) {
+        val newPermissionNames = getAllowlistedRestrictedPermissionsUnchecked(appId,
+            PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, userId
+        )?.let {
+            IndexedSet(permissionNames).apply { this += it }.toList()
+        } ?: permissionNames
+
+        setAllowlistedRestrictedPermissionsUnchecked(androidPackage, appId, newPermissionNames,
+            PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, userId)
+    }
+
     override fun removeAllowlistedRestrictedPermission(
         packageName: String,
         permissionName: String,
@@ -1445,7 +1461,7 @@
 
     private fun setAllowlistedRestrictedPermissions(
         packageName: String,
-        allowlistedPermissions: List<String>,
+        permissionNames: List<String>,
         allowlistedFlags: Int,
         userId: Int,
         isAddingPermission: Boolean
@@ -1480,7 +1496,7 @@
         }
 
         setAllowlistedRestrictedPermissionsUnchecked(
-            androidPackage, packageState.appId, allowlistedPermissions, allowlistedFlags, userId
+            androidPackage, packageState.appId, permissionNames, allowlistedFlags, userId
         )
 
         return true
@@ -1493,7 +1509,7 @@
     private fun setAllowlistedRestrictedPermissionsUnchecked(
         androidPackage: AndroidPackage,
         appId: Int,
-        allowlistedPermissions: List<String>,
+        permissionNames: List<String>,
         allowlistedFlags: Int,
         userId: Int
     ) {
@@ -1522,7 +1538,7 @@
                             PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM -> {
                                 mask = mask or PermissionFlags.SYSTEM_EXEMPT
                                 newFlags =
-                                    if (allowlistedPermissions.contains(requestedPermission)) {
+                                    if (permissionNames.contains(requestedPermission)) {
                                         newFlags or PermissionFlags.SYSTEM_EXEMPT
                                     } else {
                                         newFlags andInv PermissionFlags.SYSTEM_EXEMPT
@@ -1531,7 +1547,7 @@
                             PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE -> {
                                 mask = mask or PermissionFlags.UPGRADE_EXEMPT
                                 newFlags =
-                                    if (allowlistedPermissions.contains(requestedPermission)) {
+                                    if (permissionNames.contains(requestedPermission)) {
                                         newFlags or PermissionFlags.UPGRADE_EXEMPT
                                     } else {
                                         newFlags andInv PermissionFlags.UPGRADE_EXEMPT
@@ -1540,7 +1556,7 @@
                             PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER -> {
                                 mask = mask or PermissionFlags.INSTALLER_EXEMPT
                                 newFlags =
-                                    if (allowlistedPermissions.contains(requestedPermission)) {
+                                    if (permissionNames.contains(requestedPermission)) {
                                         newFlags or PermissionFlags.INSTALLER_EXEMPT
                                     } else {
                                         newFlags andInv PermissionFlags.INSTALLER_EXEMPT
@@ -1856,10 +1872,15 @@
         @Suppress("NAME_SHADOWING")
         userIds.forEach { userId ->
             service.onPackageInstalled(androidPackage.packageName, userId)
+        }
+
+        @Suppress("NAME_SHADOWING")
+        userIds.forEach { userId ->
             // TODO: Remove when this callback receives packageState directly.
             val packageState =
                 packageManagerInternal.getPackageStateInternal(androidPackage.packageName)!!
-            // TODO: Add allowlisting
+            addAllowlistedRestrictedPermissionsUnchecked(androidPackage, packageState.appId,
+                params.allowlistedRestrictedPermissions, userId)
             setRequestedPermissionStates(packageState, userId, params.permissionStates)
         }
     }
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
index 59551a3..5a7b37a 100644
--- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
@@ -962,7 +962,7 @@
         if (packageState.packageName == PLATFORM_PACKAGE_NAME) {
             return true
         }
-        if (!packageState.isPrivileged) {
+        if (!(packageState.isSystem && packageState.isPrivileged)) {
             return true
         }
         if (permission.packageName !in newState.systemState.privilegedPermissionAllowlistPackages) {
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index e2f56ba..94ee0a8 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -1584,11 +1584,7 @@
     @Test
     public void testConstructor_withNullContext_throws() throws Exception {
         expectThrows(
-                NullPointerException.class,
-                () ->
-                        new BackupManagerService(
-                                /* context */ null,
-                                new SparseArray<>()));
+                NullPointerException.class, () -> new BackupManagerService(/* context */ null));
     }
 
     /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
@@ -1616,18 +1612,6 @@
         verify(lifecycle).publishService(Context.BACKUP_SERVICE, backupManagerService);
     }
 
-    /** testOnUnlockUser_forwards */
-    @Test
-    public void testOnUnlockUser_forwards() {
-        BackupManagerService backupManagerService = mock(BackupManagerService.class);
-        BackupManagerService.Lifecycle lifecycle =
-                new BackupManagerService.Lifecycle(mContext, backupManagerService);
-
-        lifecycle.onUserUnlocking(new TargetUser(new UserInfo(UserHandle.USER_SYSTEM, null, 0)));
-
-        verify(backupManagerService).onUnlockUser(UserHandle.USER_SYSTEM);
-    }
-
     /** testOnStopUser_forwards */
     @Test
     public void testOnStopUser_forwards() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index cd9d8a9..b203cf6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -37,7 +37,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-
 import android.Manifest;
 import android.annotation.UserIdInt;
 import android.app.backup.BackupManager;
@@ -54,13 +53,13 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
-import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.SystemService;
 import com.android.server.backup.utils.RandomAccessFileUtils;
 
 import org.junit.After;
@@ -104,8 +103,8 @@
     private FileDescriptor mFileDescriptorStub = new FileDescriptor();
 
     private BackupManagerServiceTestable mService;
+    private BackupManagerService.Lifecycle mServiceLifecycle;
     private static File sTestDir;
-    private SparseArray<UserBackupManagerService> mUserServices;
     private MockitoSession mSession;
 
     @Before
@@ -125,8 +124,6 @@
 
         mUserId = UserHandle.USER_SYSTEM;
 
-        mUserServices = new SparseArray<>();
-
         when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
         when(mUserManagerMock.getUserInfo(NON_SYSTEM_USER)).thenReturn(mUserInfoMock);
         // Null main user means there is no main user on the device.
@@ -142,8 +139,6 @@
 
         when(mContextMock.getSystemService(Context.JOB_SCHEDULER_SERVICE))
                 .thenReturn(mock(JobScheduler.class));
-        mService = new BackupManagerServiceTestable(mContextMock, mUserServices);
-        simulateUserUnlocked(UserHandle.USER_SYSTEM);
     }
 
     @After
@@ -154,21 +149,20 @@
 
     @Test
     public void onUnlockUser_startsUserService() {
+        createBackupManagerServiceAndUnlockSystemUser();
         mService.setBackupServiceActive(NON_SYSTEM_USER, true);
         ConditionVariable unlocked = new ConditionVariable(false);
 
-        mService.onUnlockUser(NON_SYSTEM_USER);
-        mService.getBackupHandler().post(unlocked::open);
-        unlocked.block();
+        simulateUserUnlocked(NON_SYSTEM_USER);
 
         assertNotNull(mService.getUserService(NON_SYSTEM_USER));
     }
 
     @Test
     public void startServiceForUser_backupDisabledGlobally_doesNotStartUserService() {
+        createBackupManagerServiceAndUnlockSystemUser();
         BackupManagerServiceTestable.sBackupDisabled = true;
-        BackupManagerServiceTestable service =
-                new BackupManagerServiceTestable(mContextMock, new SparseArray<>());
+        BackupManagerServiceTestable service = new BackupManagerServiceTestable(mContextMock);
 
         service.startServiceForUser(UserHandle.USER_SYSTEM);
 
@@ -177,6 +171,7 @@
 
     @Test
     public void startServiceForUser_backupNotActiveForUser_doesNotStartUserService() {
+        createBackupManagerServiceAndUnlockSystemUser();
         mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
 
         mService.startServiceForUser(UserHandle.USER_SYSTEM);
@@ -186,7 +181,8 @@
 
     @Test
     public void startServiceForUser_backupEnabledGloballyAndActiveForUser_startsUserService() {
-        setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER);
+        setMockMainUserAndCreateBackupManagerService(NON_SYSTEM_USER);
+
         mService.startServiceForUser(NON_SYSTEM_USER);
 
         assertNotNull(mService.getUserService(NON_SYSTEM_USER));
@@ -194,9 +190,9 @@
 
     @Test
     public void isBackupServiceActive_backupDisabledGlobally_returnFalse() {
+        createBackupManagerServiceAndUnlockSystemUser();
         BackupManagerServiceTestable.sBackupDisabled = true;
-        BackupManagerService service =
-                new BackupManagerServiceTestable(mContextMock, mUserServices);
+        BackupManagerService service = new BackupManagerServiceTestable(mContextMock);
         service.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
 
         assertFalse(service.isBackupServiceActive(UserHandle.USER_SYSTEM));
@@ -204,6 +200,7 @@
 
     @Test
     public void isBackupServiceActive_systemUser_isDefault_deactivated_returnsFalse() {
+        createBackupManagerServiceAndUnlockSystemUser();
         // If there's no 'main' user on the device, the default user is the system user.
         mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
 
@@ -212,20 +209,23 @@
 
     @Test
     public void isBackupServiceActive_systemUser_isNotDefault_returnsFalse() {
-        setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER);
+        createBackupManagerServiceAndUnlockSystemUser();
+        setMockMainUserAndCreateBackupManagerService(NON_SYSTEM_USER);
 
         assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void isBackupServiceActive_systemUser_isDefault_returnsTrue() {
+        createBackupManagerServiceAndUnlockSystemUser();
+
         // If there's no 'main' user on the device, the default user is the system user.
         assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void isBackupServiceActive_nonSystemUser_isDefault_systemUserDeactivated_returnsFalse() {
-        setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER);
+        setMockMainUserAndCreateBackupManagerService(NON_SYSTEM_USER);
         mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
 
         assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER));
@@ -233,7 +233,7 @@
 
     @Test
     public void isBackupServiceActive_nonSystemUser_isDefault_deactivated_returnsFalse() {
-        setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER);
+        setMockMainUserAndCreateBackupManagerService(NON_SYSTEM_USER);
         mService.setBackupServiceActive(NON_SYSTEM_USER, false);
 
         assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER));
@@ -241,19 +241,24 @@
 
     @Test
     public void isBackupServiceActive_nonSystemUser_isDefault_returnsTrue() {
-        setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER);
+        createBackupManagerServiceAndUnlockSystemUser();
+
+        setMockMainUserAndCreateBackupManagerService(NON_SYSTEM_USER);
 
         assertTrue(mService.isBackupServiceActive(NON_SYSTEM_USER));
     }
 
     @Test
     public void isBackupServiceActive_nonSystemUser_isNotDefault_notActivated_returnsFalse() {
+        createBackupManagerServiceAndUnlockSystemUser();
+
         // By default non-system non-default users are not activated.
         assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER));
     }
 
     @Test
     public void isBackupServiceActive_nonSystemUser_isNotDefault_activated_returnsTrue() {
+        createBackupManagerServiceAndUnlockSystemUser();
         mService.setBackupServiceActive(NON_SYSTEM_USER, true);
 
         assertTrue(mService.isBackupServiceActive(NON_SYSTEM_USER));
@@ -261,6 +266,7 @@
 
     @Test
     public void setBackupServiceActive_forSystemUserAndCallerSystemUid_createsService() {
+        createBackupManagerServiceAndUnlockSystemUser();
         BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID;
 
         mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
@@ -270,6 +276,7 @@
 
     @Test
     public void setBackupServiceActive_forSystemUserAndCallerRootUid_createsService() {
+        createBackupManagerServiceAndUnlockSystemUser();
         BackupManagerServiceTestable.sCallingUid = Process.ROOT_UID;
 
         mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
@@ -279,6 +286,7 @@
 
     @Test
     public void setBackupServiceActive_forSystemUserAndCallerNonRootNonSystem_throws() {
+        createBackupManagerServiceAndUnlockSystemUser();
         BackupManagerServiceTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
 
         try {
@@ -290,6 +298,7 @@
 
     @Test
     public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_createsService() {
+        createBackupManagerServiceAndUnlockSystemUser();
         simulateUserUnlocked(NON_SYSTEM_USER);
         when(mUserInfoMock.isManagedProfile()).thenReturn(true);
         BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID;
@@ -301,6 +310,7 @@
 
     @Test
     public void setBackupServiceActive_forManagedProfileAndCallerRootUid_createsService() {
+        createBackupManagerServiceAndUnlockSystemUser();
         simulateUserUnlocked(NON_SYSTEM_USER);
         when(mUserInfoMock.isManagedProfile()).thenReturn(true);
         BackupManagerServiceTestable.sCallingUid = Process.ROOT_UID;
@@ -312,6 +322,7 @@
 
     @Test
     public void setBackupServiceActive_forManagedProfileAndCallerNonRootNonSystem_throws() {
+        createBackupManagerServiceAndUnlockSystemUser();
         when(mUserInfoMock.isManagedProfile()).thenReturn(true);
         BackupManagerServiceTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
 
@@ -324,6 +335,7 @@
 
     @Test
     public void setBackupServiceActive_forNonSystemUserAndCallerWithoutBackupPermission_throws() {
+        createBackupManagerServiceAndUnlockSystemUser();
         doThrow(new SecurityException())
                 .when(mContextMock)
                 .enforceCallingOrSelfPermission(eq(Manifest.permission.BACKUP), anyString());
@@ -337,6 +349,7 @@
 
     @Test
     public void setBackupServiceActive_forNonSystemUserAndCallerWithoutUserPermission_throws() {
+        createBackupManagerServiceAndUnlockSystemUser();
         doThrow(new SecurityException())
                 .when(mContextMock)
                 .enforceCallingOrSelfPermission(
@@ -351,9 +364,9 @@
 
     @Test
     public void setBackupServiceActive_backupDisabledGlobally_ignored() {
+        createBackupManagerServiceAndUnlockSystemUser();
         BackupManagerServiceTestable.sBackupDisabled = true;
-        BackupManagerServiceTestable service =
-                new BackupManagerServiceTestable(mContextMock, mUserServices);
+        BackupManagerServiceTestable service = new BackupManagerServiceTestable(mContextMock);
 
         service.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
 
@@ -362,6 +375,7 @@
 
     @Test
     public void setBackupServiceActive_alreadyActive_ignored() {
+        createBackupManagerServiceAndUnlockSystemUser();
         mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
         assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
 
@@ -371,6 +385,7 @@
 
     @Test
     public void setBackupServiceActive_systemUser_makeActive_deletesSuppressFile() {
+        createBackupManagerServiceAndUnlockSystemUser();
         mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
 
         mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
@@ -380,6 +395,7 @@
 
     @Test
     public void setBackupServiceActive_systemUser_makeNonActive_createsSuppressFile() {
+        createBackupManagerServiceAndUnlockSystemUser();
         mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
 
         assertTrue(getFakeSuppressFileForUser(UserHandle.USER_SYSTEM).exists());
@@ -387,6 +403,7 @@
 
     @Test
     public void setBackupServiceActive_systemUser_makeNonActive_stopsUserService() {
+        createBackupManagerServiceAndUnlockSystemUser();
         assertTrue(mService.isUserReadyForBackup(UserHandle.USER_SYSTEM));
 
         mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
@@ -396,7 +413,7 @@
 
     @Test
     public void setBackupServiceActive_nonSystemUser_isDefault_makeActive_createsService() {
-        setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER);
+        setMockMainUserAndCreateBackupManagerService(NON_SYSTEM_USER);
         simulateUserUnlocked(NON_SYSTEM_USER);
         mService.setBackupServiceActive(NON_SYSTEM_USER, false);
 
@@ -407,7 +424,7 @@
 
     @Test
     public void setBackupServiceActive_nonSystemUser_isDefault_makeActive_deletesSuppressFile() {
-        setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER);
+        setMockMainUserAndCreateBackupManagerService(NON_SYSTEM_USER);
         simulateUserUnlocked(NON_SYSTEM_USER);
         mService.setBackupServiceActive(NON_SYSTEM_USER, false);
 
@@ -418,7 +435,7 @@
 
     @Test
     public void setBackupServiceActive_nonSystemUser_isDefault_makeNonActive_createsSuppressFile() {
-        setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER);
+        setMockMainUserAndCreateBackupManagerService(NON_SYSTEM_USER);
         mService.setBackupServiceActive(NON_SYSTEM_USER, false);
 
         assertTrue(getFakeSuppressFileForUser(NON_SYSTEM_USER).exists());
@@ -426,7 +443,7 @@
 
     @Test
     public void setBackupServiceActive_nonSystemUser_isDefault_makeNonActive_stopsUserService() {
-        setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER);
+        setMockMainUserAndCreateBackupManagerService(NON_SYSTEM_USER);
         simulateUserUnlocked(NON_SYSTEM_USER);
         assertTrue(mService.isUserReadyForBackup(NON_SYSTEM_USER));
 
@@ -437,6 +454,7 @@
 
     @Test
     public void setBackupServiceActive_nonSystemUser_isNotDefault_makeActive_createsService() {
+        createBackupManagerServiceAndUnlockSystemUser();
         simulateUserUnlocked(NON_SYSTEM_USER);
 
         mService.setBackupServiceActive(NON_SYSTEM_USER, true);
@@ -446,6 +464,8 @@
 
     @Test
     public void setBackupServiceActive_nonSystemUser_isNotDefault_makeActive_createActivatedFile() {
+        createBackupManagerServiceAndUnlockSystemUser();
+
         mService.setBackupServiceActive(NON_SYSTEM_USER, true);
 
         assertTrue(getFakeActivatedFileForUser(NON_SYSTEM_USER).exists());
@@ -453,6 +473,7 @@
 
     @Test
     public void setBackupServiceActive_nonSystemUser_isNotDefault_makeNonActive_stopsUserService() {
+        createBackupManagerServiceAndUnlockSystemUser();
         simulateUserUnlocked(NON_SYSTEM_USER);
         mService.setBackupServiceActive(NON_SYSTEM_USER, true);
 
@@ -463,6 +484,7 @@
 
     @Test
     public void setBackupServiceActive_nonSystemUser_isNotDefault_makeActive_deleteActivatedFile() {
+        createBackupManagerServiceAndUnlockSystemUser();
         mService.setBackupServiceActive(NON_SYSTEM_USER, true);
 
         mService.setBackupServiceActive(NON_SYSTEM_USER, false);
@@ -472,6 +494,7 @@
 
     @Test
     public void setBackupServiceActive_forOneNonSystemUser_doesNotActivateForAllNonSystemUsers() {
+        createBackupManagerServiceAndUnlockSystemUser();
         int otherUser = NON_SYSTEM_USER + 1;
         mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
 
@@ -482,6 +505,8 @@
 
     @Test
     public void setBackupServiceActive_forNonSystemUser_remembersActivated() {
+        createBackupManagerServiceAndUnlockSystemUser();
+
         mService.setBackupServiceActive(NON_SYSTEM_USER, true);
 
         assertTrue(RandomAccessFileUtils.readBoolean(
@@ -490,6 +515,8 @@
 
     @Test
     public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() {
+        createBackupManagerServiceAndUnlockSystemUser();
+
         mService.setBackupServiceActive(NON_SYSTEM_USER, false);
 
         assertFalse(RandomAccessFileUtils.readBoolean(
@@ -498,6 +525,8 @@
 
     @Test
     public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() {
+        createBackupManagerServiceAndUnlockSystemUser();
+
         mService.setBackupServiceActive(NON_SYSTEM_USER, true);
         mService.setBackupServiceActive(NON_SYSTEM_USER, false);
 
@@ -508,7 +537,7 @@
     @Test
     public void selectBackupTransportAsyncForUser_beforeUserUnlocked_notifiesBackupNotAllowed()
             throws Exception {
-        mUserServices.clear();
+        mService = new BackupManagerServiceTestable(mContextMock);
         CompletableFuture<Integer> future = new CompletableFuture<>();
         ISelectBackupTransportCallback listener =
                 new ISelectBackupTransportCallback.Stub() {
@@ -531,6 +560,8 @@
     @Test
     public void selectBackupTransportAsyncForUser_beforeUserUnlockedWithNullListener_doesNotThrow()
             throws Exception {
+        createBackupManagerServiceAndUnlockSystemUser();
+
         mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
 
         // No crash.
@@ -539,6 +570,8 @@
     @Test
     public void selectBackupTransportAsyncForUser_beforeUserUnlockedListenerThrowing_doesNotThrow()
             throws Exception {
+        createBackupManagerServiceAndUnlockSystemUser();
+
         ISelectBackupTransportCallback.Stub listener =
                 new ISelectBackupTransportCallback.Stub() {
                     @Override
@@ -558,6 +591,7 @@
 
     @Test
     public void dump_callerDoesNotHaveDumpPermission_ignored() {
+        createBackupManagerServiceAndUnlockSystemUser();
         when(mContextMock.checkCallingOrSelfPermission(
                 Manifest.permission.DUMP)).thenReturn(
                 PackageManager.PERMISSION_DENIED);
@@ -570,6 +604,7 @@
 
     @Test
     public void dump_callerDoesNotHavePackageUsageStatsPermission_ignored() {
+        createBackupManagerServiceAndUnlockSystemUser();
         when(mContextMock.checkCallingOrSelfPermission(
                 Manifest.permission.PACKAGE_USAGE_STATS)).thenReturn(
                 PackageManager.PERMISSION_DENIED);
@@ -586,6 +621,7 @@
      */
     @Test
     public void testDump_systemUserFirst() {
+        createBackupManagerServiceAndUnlockSystemUser();
         mService.setBackupServiceActive(NON_SYSTEM_USER, true);
         simulateUserUnlocked(NON_SYSTEM_USER);
         String[] args = new String[0];
@@ -602,6 +638,7 @@
 
     @Test
     public void testGetUserForAncestralSerialNumber_forSystemUser() {
+        createBackupManagerServiceAndUnlockSystemUser();
         simulateUserUnlocked(NON_SYSTEM_USER);
         when(mUserManagerMock.getProfileIds(UserHandle.getCallingUserId(), false))
                 .thenReturn(new int[]{UserHandle.USER_SYSTEM, NON_SYSTEM_USER});
@@ -614,10 +651,10 @@
 
     @Test
     public void testGetUserForAncestralSerialNumber_forNonSystemUser() {
-        setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER);
+        setMockMainUserAndCreateBackupManagerService(NON_SYSTEM_USER);
         simulateUserUnlocked(NON_SYSTEM_USER);
         when(mUserManagerMock.getProfileIds(UserHandle.getCallingUserId(), false))
-                .thenReturn(new int[]{UserHandle.USER_SYSTEM, NON_SYSTEM_USER});
+                .thenReturn(new int[] {UserHandle.USER_SYSTEM, NON_SYSTEM_USER});
         when(mNonSystemUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
 
         UserHandle user = mService.getUserForAncestralSerialNumber(11L);
@@ -627,9 +664,9 @@
 
     @Test
     public void testGetUserForAncestralSerialNumber_whenDisabled() {
+        createBackupManagerServiceAndUnlockSystemUser();
         BackupManagerServiceTestable.sBackupDisabled = true;
-        BackupManagerService backupManagerService =
-                new BackupManagerServiceTestable(mContextMock, mUserServices);
+        BackupManagerService backupManagerService = new BackupManagerServiceTestable(mContextMock);
         when(mSystemUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
 
         UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L);
@@ -637,18 +674,68 @@
         assertThat(user).isNull();
     }
 
+    @Test
+    public void onUserUnlocking_mainUserChanged_firstUnlockAfterReboot_updatesDefaultUser() {
+        // Create BMS *before* setting a main user to simulate the main user being created after
+        // BMS, which can happen for the first ever boot of a new device.
+        mService = new BackupManagerServiceTestable(mContextMock);
+        mServiceLifecycle = new BackupManagerService.Lifecycle(mContextMock, mService);
+        when(mUserManagerMock.getMainUser()).thenReturn(UserHandle.of(NON_SYSTEM_USER));
+        assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER));
+
+        simulateUserUnlocked(UserHandle.USER_SYSTEM);
+
+        assertTrue(mService.isBackupServiceActive(NON_SYSTEM_USER));
+    }
+
+    @Test
+    public void onUserUnlocking_mainUserChanged_firstUnlockAfterReboot_doesNotStartForSystemUser() {
+        // Create BMS *before* setting a main user to simulate the main user being created after
+        // BMS, which can happen for the first ever boot of a new device.
+        mService = new BackupManagerServiceTestable(mContextMock);
+        mServiceLifecycle = new BackupManagerService.Lifecycle(mContextMock, mService);
+        when(mUserManagerMock.getMainUser()).thenReturn(UserHandle.of(NON_SYSTEM_USER));
+        assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER));
+
+        simulateUserUnlocked(UserHandle.USER_SYSTEM);
+
+        assertFalse(mService.isUserReadyForBackup(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void onUserUnlocking_mainUserChanged_secondUnlockAfterReboot_doesNotUpdateDefaultUser() {
+        // Create BMS *before* setting a main user to simulate the main user being created after
+        // BMS, which can happen for the first ever boot of a new device.
+        createBackupManagerServiceAndUnlockSystemUser();
+        when(mUserManagerMock.getMainUser()).thenReturn(UserHandle.of(NON_SYSTEM_USER));
+        assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER));
+
+        simulateUserUnlocked(NON_SYSTEM_USER);
+
+        assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER));
+    }
+
+    private void createBackupManagerServiceAndUnlockSystemUser() {
+        mService = new BackupManagerServiceTestable(mContextMock);
+        mServiceLifecycle = new BackupManagerService.Lifecycle(mContextMock, mService);
+        simulateUserUnlocked(UserHandle.USER_SYSTEM);
+    }
+
     /**
      * The 'default' user is set in the constructor of {@link BackupManagerService} so we need to
      * start a new service after mocking the 'main' user.
      */
-    private void setMockMainUserAndStartNewBackupManagerService(int userId) {
+    private void setMockMainUserAndCreateBackupManagerService(int userId) {
         when(mUserManagerMock.getMainUser()).thenReturn(UserHandle.of(userId));
-        mService = new BackupManagerServiceTestable(mContextMock, mUserServices);
+        mService = new BackupManagerServiceTestable(mContextMock);
+        mServiceLifecycle = new BackupManagerService.Lifecycle(mContextMock, mService);
     }
 
     private void simulateUserUnlocked(int userId) {
         ConditionVariable unlocked = new ConditionVariable(false);
-        mService.onUnlockUser(userId);
+        mServiceLifecycle.onUserUnlocking(
+                new SystemService.TargetUser(
+                        new UserInfo(userId, /* name= */ "test", /* flags= */ 0)));
         mService.getBackupHandler().post(unlocked::open);
         unlocked.block();
         when(mUserManagerMock.isUserUnlocked(userId)).thenReturn(true);
@@ -672,9 +759,8 @@
         static int sCallingUid = -1;
         static UserManager sUserManagerMock = null;
 
-        BackupManagerServiceTestable(
-                Context context, SparseArray<UserBackupManagerService> userServices) {
-            super(context, userServices);
+        BackupManagerServiceTestable(Context context) {
+            super(context);
         }
 
         @Override
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 819b0b2..66711df 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -18038,6 +18038,87 @@
     public static final int CELL_BROADCAST_RESULT_FAIL_ACTIVATION = 3;
 
     /**
+     * Callback mode type
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"EMERGENCY_CALLBACK_MODE_"}, value = {
+            EMERGENCY_CALLBACK_MODE_CALL,
+            EMERGENCY_CALLBACK_MODE_SMS})
+    public @interface EmergencyCallbackModeType {}
+
+    /**
+     * The callback mode is due to emergency call.
+     * @hide
+     */
+    public static final int EMERGENCY_CALLBACK_MODE_CALL = 1;
+
+    /**
+     * The callback mode is due to emergency SMS.
+     * @hide
+     */
+    public static final int EMERGENCY_CALLBACK_MODE_SMS = 2;
+
+    /**
+     * The reason for changing callback mode.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"STOP_REASON_"},
+            value = {
+                    STOP_REASON_UNKNOWN,
+                    STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED,
+                    STOP_REASON_NORMAL_SMS_SENT,
+                    STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED,
+                    STOP_REASON_EMERGENCY_SMS_SENT,
+                    STOP_REASON_TIMER_EXPIRED,
+                    STOP_REASON_USER_ACTION,
+            })
+    public @interface EmergencyCallbackModeStopReason {}
+
+    /**
+     * unknown reason.
+     * @hide
+     */
+    public static final int STOP_REASON_UNKNOWN = 0;
+
+    /**
+     * The call back mode is exited due to a new normal call is originated.
+     * @hide
+     */
+    public static final int STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED = 1;
+
+    /**
+     * The call back mode is exited due to a new normal SMS is originated.
+     * @hide
+     */
+    public static final int STOP_REASON_NORMAL_SMS_SENT = 2;
+
+    /**
+     * The call back mode is exited due to a new emergency call is originated.
+     * @hide
+     */
+    public static final int STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED = 3;
+
+    /**
+     * The call back mode is exited due to a new emergency SMS is originated.
+     * @hide
+     */
+    public static final int STOP_REASON_EMERGENCY_SMS_SENT = 4;
+
+    /**
+     * The call back mode is exited due to timer expiry.
+     * @hide
+     */
+    public static final int STOP_REASON_TIMER_EXPIRED = 5;
+
+    /**
+     * The call back mode is exited due to user action.
+     * @hide
+     */
+    public static final int STOP_REASON_USER_ACTION = 6;
+
+    /**
      * Set reception of cell broadcast messages with the list of the given ranges
      *
      * <p>The ranges set previously will be overridden by the new one. Empty list
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index 74b42d4..9593c8a 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -340,16 +340,18 @@
      * Send command to the implementation of {@link SharedConnectivityService} requesting
      * disconnection from the active Tether Network.
      *
+     * @param network {@link TetherNetwork} object representing the network the user has requested
+     *                to disconnect from.
      * @return Returns true if the service received the command. Does not guarantee that the
      *         disconnection was successful.
      */
-    public boolean disconnectTetherNetwork() {
+    public boolean disconnectTetherNetwork(@NonNull TetherNetwork network) {
         if (mService == null) {
             return false;
         }
 
         try {
-            mService.disconnectTetherNetwork();
+            mService.disconnectTetherNetwork(network);
         } catch (RemoteException e) {
             Log.e(TAG, "Exception in disconnectTetherNetwork", e);
             return false;
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl
index 5d79405..52da596 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl
@@ -27,7 +27,7 @@
     void registerCallback(in ISharedConnectivityCallback callback);
     void unregisterCallback(in ISharedConnectivityCallback callback);
     void connectTetherNetwork(in TetherNetwork network);
-    void disconnectTetherNetwork();
+    void disconnectTetherNetwork(in TetherNetwork network);
     void connectKnownNetwork(in KnownNetwork network);
     void forgetKnownNetwork(in KnownNetwork network);
 }
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index a0b931f..13084f4 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -117,9 +117,9 @@
             }
 
             @Override
-            public void disconnectTetherNetwork() {
+            public void disconnectTetherNetwork(TetherNetwork network) {
                 checkPermissions();
-                mHandler.post(() -> onDisconnectTetherNetwork());
+                mHandler.post(() -> onDisconnectTetherNetwork(network));
             }
 
             @Override
@@ -323,8 +323,10 @@
      * Implementing application should implement this method.
      *
      * Implementation should initiate a disconnection from the active Tether Network.
+     *
+     * @param network Object identifying the Tether Network the user has requested to disconnect.
      */
-    public abstract void onDisconnectTetherNetwork();
+    public abstract void onDisconnectTetherNetwork(@NonNull TetherNetwork network);
 
     /**
      * Implementing application should implement this method.
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
index 815a012..439d456 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
@@ -285,25 +285,28 @@
      */
     @Test
     public void disconnectTetherNetwork_serviceNotConnected_shouldFail() {
+        TetherNetwork network = buildTetherNetwork();
         SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(null);
-        assertFalse(manager.disconnectTetherNetwork());
+        assertFalse(manager.disconnectTetherNetwork(network));
     }
 
     @Test
     public void disconnectTetherNetwork() throws RemoteException {
+        TetherNetwork network = buildTetherNetwork();
         SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(mService);
-        manager.disconnectTetherNetwork();
-        verify(mService).disconnectTetherNetwork();
+        manager.disconnectTetherNetwork(network);
+        verify(mService).disconnectTetherNetwork(network);
     }
 
     @Test
     public void disconnectTetherNetwork_remoteException_shouldFail() throws RemoteException {
+        TetherNetwork network = buildTetherNetwork();
         SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(mService);
-        doThrow(new RemoteException()).when(mService).disconnectTetherNetwork();
-        assertFalse(manager.disconnectTetherNetwork());
+        doThrow(new RemoteException()).when(mService).disconnectTetherNetwork(any());
+        assertFalse(manager.disconnectTetherNetwork(network));
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
index e15be8b..fb8d7bf 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
@@ -56,7 +56,7 @@
             public void onConnectTetherNetwork(TetherNetwork network) {}
 
             @Override
-            public void onDisconnectTetherNetwork() {}
+            public void onDisconnectTetherNetwork(TetherNetwork network) {}
 
             @Override
             public void onConnectKnownNetwork(KnownNetwork network) {}