Merge "Stop getting locale config overrides" into main
diff --git a/Android.bp b/Android.bp
index f6a9328..b139b7e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -132,6 +132,7 @@
         ":libcamera_client_aidl",
         ":libcamera_client_framework_aidl",
         ":libupdate_engine_aidl",
+        ":libupdate_engine_stable-V2-java-source",
         ":logd_aidl",
         ":resourcemanager_aidl",
         ":storaged_aidl",
diff --git a/core/api/current.txt b/core/api/current.txt
index 8a4898c..c7b921c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -22102,7 +22102,7 @@
   }
 
   @FlaggedApi("android.media.audio.loudness_configurator_api") public class LoudnessCodecConfigurator {
-    method @FlaggedApi("android.media.audio.loudness_configurator_api") public void addMediaCodec(@NonNull android.media.MediaCodec);
+    method @FlaggedApi("android.media.audio.loudness_configurator_api") public boolean addMediaCodec(@NonNull android.media.MediaCodec);
     method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create();
     method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create(@NonNull java.util.concurrent.Executor, @NonNull android.media.LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener);
     method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public android.os.Bundle getLoudnessCodecParams(@NonNull android.media.AudioTrack, @NonNull android.media.MediaCodec);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7c52238..ee1d117b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4207,7 +4207,9 @@
      * new state of quiet mode. This is only sent to registered receivers, not manifest receivers.
      *
      * <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_AVAILABLE} but functions as a
-     * generic broadcast for all profile users.
+     * generic broadcast for all users of type {@link android.os.UserManager#isProfile()}}. In
+     * case of a managed profile, both {@link #ACTION_MANAGED_PROFILE_AVAILABLE} and
+     * {@link #ACTION_PROFILE_AVAILABLE} broadcasts are sent.
      */
     @FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE)
     public static final String ACTION_PROFILE_AVAILABLE =
@@ -4221,7 +4223,9 @@
      * new state of quiet mode. This is only sent to registered receivers, not manifest receivers.
      *
      * <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_UNAVAILABLE} but functions as
-     * a generic broadcast for all profile users.
+     * a generic broadcast for all users of type {@link android.os.UserManager#isProfile()}}. In
+     * case of a managed profile, both {@link #ACTION_MANAGED_PROFILE_UNAVAILABLE} and
+     * {@link #ACTION_PROFILE_UNAVAILABLE} broadcasts are sent.
      */
     @FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE)
     public static final String ACTION_PROFILE_UNAVAILABLE =
diff --git a/core/java/android/hardware/face/FaceAuthenticateOptions.java b/core/java/android/hardware/face/FaceAuthenticateOptions.java
index 1c6de04..518f902a 100644
--- a/core/java/android/hardware/face/FaceAuthenticateOptions.java
+++ b/core/java/android/hardware/face/FaceAuthenticateOptions.java
@@ -261,7 +261,7 @@
      * The reason for this operation when requested by the system (sysui),
      * otherwise AUTHENTICATE_REASON_UNKNOWN.
      *
-     * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+     * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
      * for more details about each reason.
      */
     @DataClass.Generated.Member
@@ -524,7 +524,7 @@
          * The reason for this operation when requested by the system (sysui),
          * otherwise AUTHENTICATE_REASON_UNKNOWN.
          *
-         * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+         * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
          * for more details about each reason.
          */
         @DataClass.Generated.Member
diff --git a/core/java/android/os/UpdateEngineStable.java b/core/java/android/os/UpdateEngineStable.java
new file mode 100644
index 0000000..9e2593e
--- /dev/null
+++ b/core/java/android/os/UpdateEngineStable.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+
+/**
+ * UpdateEngineStable handles calls to the update engine stalbe which takes care of A/B OTA updates.
+ * This interface has lesser functionalities than UpdateEngine and doesn't allow cancel.
+ *
+ * <p>The minimal flow is:
+ *
+ * <ol>
+ *   <li>Create a new UpdateEngineStable instance.
+ *   <li>Call {@link #bind}, provide callback function.
+ *   <li>Call {@link #applyPayloadFd}.
+ * </ol>
+ *
+ * The APIs defined in this class and UpdateEngineStableCallback class must be in sync with the ones
+ * in {@code system/update_engine/stable/android/os/IUpdateEngineStable.aidl} and {@code
+ * ssystem/update_engine/stable/android/os/IUpdateEngineStableCallback.aidl}.
+ *
+ * @hide
+ */
+public class UpdateEngineStable {
+    private static final String TAG = "UpdateEngineStable";
+
+    private static final String UPDATE_ENGINE_STABLE_SERVICE =
+            "android.os.UpdateEngineStableService";
+
+    /**
+     * Error codes from update engine upon finishing a call to {@link applyPayloadFd}. Values will
+     * be passed via the callback function {@link
+     * UpdateEngineStableCallback#onPayloadApplicationComplete}. Values must agree with the ones in
+     * {@code system/update_engine/common/error_code.h}.
+     */
+    /** @hide */
+    @IntDef(
+            value = {
+                UpdateEngine.ErrorCodeConstants.SUCCESS,
+                UpdateEngine.ErrorCodeConstants.ERROR,
+                UpdateEngine.ErrorCodeConstants.FILESYSTEM_COPIER_ERROR,
+                UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+                UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+                UpdateEngine.ErrorCodeConstants.KERNEL_DEVICE_OPEN_ERROR,
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
+                UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+                UpdateEngine.ErrorCodeConstants.NOT_ENOUGH_SPACE,
+                UpdateEngine.ErrorCodeConstants.DEVICE_CORRUPTED,
+            })
+    public @interface ErrorCode {}
+
+    private final IUpdateEngineStable mUpdateEngineStable;
+    private IUpdateEngineStableCallback mUpdateEngineStableCallback = null;
+    private final Object mUpdateEngineStableCallbackLock = new Object();
+
+    /**
+     * Creates a new instance.
+     *
+     * @hide
+     */
+    public UpdateEngineStable() {
+        mUpdateEngineStable =
+                IUpdateEngineStable.Stub.asInterface(
+                        ServiceManager.getService(UPDATE_ENGINE_STABLE_SERVICE));
+        if (mUpdateEngineStable == null) {
+            throw new IllegalStateException("Failed to find " + UPDATE_ENGINE_STABLE_SERVICE);
+        }
+    }
+
+    /**
+     * Prepares this instance for use. The callback will be notified on any status change, and when
+     * the update completes. A handler can be supplied to control which thread runs the callback, or
+     * null.
+     *
+     * @hide
+     */
+    public boolean bind(final UpdateEngineStableCallback callback, final Handler handler) {
+        synchronized (mUpdateEngineStableCallbackLock) {
+            mUpdateEngineStableCallback =
+                    new IUpdateEngineStableCallback.Stub() {
+                        @Override
+                        public void onStatusUpdate(final int status, final float percent) {
+                            if (handler != null) {
+                                handler.post(
+                                        new Runnable() {
+                                            @Override
+                                            public void run() {
+                                                callback.onStatusUpdate(status, percent);
+                                            }
+                                        });
+                            } else {
+                                callback.onStatusUpdate(status, percent);
+                            }
+                        }
+
+                        @Override
+                        public void onPayloadApplicationComplete(final int errorCode) {
+                            if (handler != null) {
+                                handler.post(
+                                        new Runnable() {
+                                            @Override
+                                            public void run() {
+                                                callback.onPayloadApplicationComplete(errorCode);
+                                            }
+                                        });
+                            } else {
+                                callback.onPayloadApplicationComplete(errorCode);
+                            }
+                        }
+
+                        @Override
+                        public int getInterfaceVersion() {
+                            return super.VERSION;
+                        }
+
+                        @Override
+                        public String getInterfaceHash() {
+                            return super.HASH;
+                        }
+                    };
+
+            try {
+                return mUpdateEngineStable.bind(mUpdateEngineStableCallback);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Equivalent to {@code bind(callback, null)}.
+     *
+     * @hide
+     */
+    public boolean bind(final UpdateEngineStableCallback callback) {
+        return bind(callback, null);
+    }
+
+    /**
+     * Applies payload from given ParcelFileDescriptor. Usage is same as UpdateEngine#applyPayload
+     *
+     * @hide
+     */
+    public void applyPayloadFd(
+            ParcelFileDescriptor fd, long offset, long size, String[] headerKeyValuePairs) {
+        try {
+            mUpdateEngineStable.applyPayloadFd(fd, offset, size, headerKeyValuePairs);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unbinds the last bound callback function.
+     *
+     * @hide
+     */
+    public boolean unbind() {
+        synchronized (mUpdateEngineStableCallbackLock) {
+            if (mUpdateEngineStableCallback == null) {
+                return true;
+            }
+            try {
+                boolean result = mUpdateEngineStable.unbind(mUpdateEngineStableCallback);
+                mUpdateEngineStableCallback = null;
+                return result;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+}
diff --git a/core/java/android/os/UpdateEngineStableCallback.java b/core/java/android/os/UpdateEngineStableCallback.java
new file mode 100644
index 0000000..4bcfb4b
--- /dev/null
+++ b/core/java/android/os/UpdateEngineStableCallback.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Callback function for UpdateEngineStable. Used to keep the caller up to date with progress, so
+ * the UI (if any) can be updated.
+ *
+ * <p>The APIs defined in this class and UpdateEngineStable class must be in sync with the ones in
+ * system/update_engine/stable/android/os/IUpdateEngineStable.aidl and
+ * system/update_engine/stable/android/os/IUpdateEngineStableCallback.aidl.
+ *
+ * <p>{@hide}
+ */
+public abstract class UpdateEngineStableCallback {
+
+    /**
+     * Invoked when anything changes. The value of {@code status} will be one of the values from
+     * {@link UpdateEngine.UpdateStatusConstants}, and {@code percent} will be valid
+     *
+     * @hide
+     */
+    public abstract void onStatusUpdate(int status, float percent);
+
+    /**
+     * Invoked when the payload has been applied, whether successfully or unsuccessfully. The value
+     * of {@code errorCode} will be one of the values from {@link UpdateEngine.ErrorCodeConstants}.
+     *
+     * @hide
+     */
+    public abstract void onPayloadApplicationComplete(@UpdateEngineStable.ErrorCode int errorCode);
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 54cc5f4..84fddcb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7428,6 +7428,20 @@
         public static final String DEFAULT_INPUT_METHOD = "default_input_method";
 
         /**
+         * Used only by {@link com.android.server.inputmethod.InputMethodManagerService} as a
+         * temporary data store of {@link #DEFAULT_INPUT_METHOD} while a virtual-device-specific
+         * input method is set as default.</p>
+         *
+         * <p>This should be considered to be an implementation detail of
+         * {@link com.android.server.inputmethod.InputMethodManagerService}.  Other system
+         * components should never rely on this value.</p>
+         *
+         * @see #DEFAULT_INPUT_METHOD
+         * @hide
+         */
+        public static final String DEFAULT_DEVICE_INPUT_METHOD = "default_device_input_method";
+
+        /**
          * Setting to record the input method subtype used by default, holding the ID
          * of the desired method.
          */
@@ -14347,6 +14361,19 @@
                 "mute_alarm_stream_with_ringer_mode";
 
         /**
+         * The user's choice for whether or not Alarm stream should always be muted with Ringer.
+         *
+         * <p>Note that this is different from {@link #MUTE_ALARM_STREAM_WITH_RINGER_MODE}, which
+         * controls the real state of whether or not the Alarm stream and Ringer association occurs.
+         * The two Settings are not necessarily equal, if the final decision for the association
+         * depends on factors beyond the user's preference.
+         *
+         * @hide
+         */
+        public static final String MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE =
+                "mute_alarm_stream_with_ringer_mode_user_preference";
+
+        /**
          * Overlay display devices setting.
          * The associated value is a specially formatted string that describes the
          * size and density of simulated secondary display devices.
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index e4af2da..d47ff2e 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -368,11 +368,13 @@
          * <p>
          * As of Android 11 apps will need specific permission to query other packages. To use
          * this method an app must include in their AndroidManifest:
+         * <pre>{@code
          * <queries>
          *   <intent>
          *     <action android:name="android.provider.Telephony.SMS_DELIVER"/>
          *   </intent>
          * </queries>
+         * }</pre>
          * Which will allow them to query packages which declare intent filters that include
          * the {@link android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION} intent.
          * </p>
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 692dad4..1a2be15 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1439,12 +1439,12 @@
                                 // to make sure the wallpaper is stopped even after the events
                                 // onSurfaceCreated() and onSurfaceChanged().
                                 if (noConsecutiveVisibilityEvents()) {
-                                    if (DEBUG) Log.v(TAG, "toggling doVisibilityChanged");
-                                    Trace.beginSection("WPMS.Engine.doVisibilityChanged-true");
-                                    doVisibilityChanged(true);
+                                    if (DEBUG) Log.v(TAG, "toggling onVisibilityChanged");
+                                    Trace.beginSection("WPMS.Engine.onVisibilityChanged-true");
+                                    onVisibilityChanged(true);
                                     Trace.endSection();
-                                    Trace.beginSection("WPMS.Engine.doVisibilityChanged-false");
-                                    doVisibilityChanged(false);
+                                    Trace.beginSection("WPMS.Engine.onVisibilityChanged-false");
+                                    onVisibilityChanged(false);
                                     Trace.endSection();
                                 } else {
                                     if (DEBUG) {
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 216acdc..3366a7e 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -38,6 +38,14 @@
 }
 
 flag {
+  name: "draw_magnifier_border_outside_wmlock"
+  namespace: "windowing_frontend"
+  description: "Avoid holding WM locks for a long time when executing lockCanvas"
+  bug: "316075123"
+  is_fixed_read_only: true
+}
+
+flag {
   name: "introduce_smoother_dimmer"
   namespace: "windowing_frontend"
   description: "Refactor dim to fix flickers"
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 65597de..066f38b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -819,14 +819,20 @@
             Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId);
             return;
         }
+        // Checks if container should be updated before apply new parentInfo.
+        final boolean shouldUpdateContainer = taskContainer.shouldUpdateContainer(parentInfo);
         taskContainer.updateTaskFragmentParentInfo(parentInfo);
         if (!taskContainer.isVisible()) {
             // Don't update containers if the task is not visible. We only update containers when
             // parentInfo#isVisibleRequested is true.
             return;
         }
-        if (isInPictureInPicture(parentInfo.getConfiguration())) {
-            // No need to update presentation in PIP until the Task exit PIP.
+
+        // If the last direct activity of the host task is dismissed and the overlay container is
+        // the only taskFragment, the overlay container should also be dismissed.
+        dismissOverlayContainerIfNeeded(wct, taskContainer);
+
+        if (!shouldUpdateContainer) {
             return;
         }
         updateContainersInTask(wct, taskContainer);
@@ -1947,11 +1953,8 @@
     void updateOverlayContainer(@NonNull WindowContainerTransaction wct,
             @NonNull TaskFragmentContainer container) {
         final TaskContainer taskContainer = container.getTaskContainer();
-        // Dismiss the overlay container if it's the only container in the task and there's no
-        // direct activity in the parent task.
-        if (taskContainer.getTaskFragmentContainers().size() == 1
-                && !taskContainer.hasDirectActivity()) {
-            container.finish(false /* shouldFinishDependent */, mPresenter, wct, this);
+
+        if (dismissOverlayContainerIfNeeded(wct, taskContainer)) {
             return;
         }
 
@@ -1968,6 +1971,24 @@
         }
     }
 
+    /** Dismisses the overlay container in the {@code taskContainer} if needed. */
+    @GuardedBy("mLock")
+    private boolean dismissOverlayContainerIfNeeded(@NonNull WindowContainerTransaction wct,
+            @NonNull TaskContainer taskContainer) {
+        final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer();
+        if (overlayContainer == null) {
+            return false;
+        }
+        // Dismiss the overlay container if it's the only container in the task and there's no
+        // direct activity in the parent task.
+        if (taskContainer.getTaskFragmentContainers().size() == 1
+                && !taskContainer.hasDirectActivity()) {
+            mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */);
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Updates {@link SplitContainer} with the given {@link SplitAttributes} if the
      * {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 64ad4fa..71195b6 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -138,6 +138,21 @@
     }
 
     /**
+     * Returns {@code true} if the container should be updated with {@code info}.
+     */
+    boolean shouldUpdateContainer(@NonNull TaskFragmentParentInfo info) {
+        final Configuration configuration = info.getConfiguration();
+
+        return info.isVisible()
+                // No need to update presentation in PIP until the Task exit PIP.
+                && !isInPictureInPicture(configuration)
+                // If the task properties equals regardless of starting position, don't need to
+                // update the container.
+                && (mConfiguration.diffPublicOnly(configuration) != 0
+                || mDisplayId != info.getDisplayId());
+    }
+
+    /**
      * Returns the windowing mode for the TaskFragments below this Task, which should be split with
      * other TaskFragments.
      *
@@ -161,7 +176,11 @@
     }
 
     boolean isInPictureInPicture() {
-        return getWindowingMode() == WINDOWING_MODE_PINNED;
+        return isInPictureInPicture(mConfiguration);
+    }
+
+    private static boolean isInPictureInPicture(@NonNull Configuration configuration) {
+        return configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
     }
 
     boolean isInMultiWindow() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index b52971a..6fe8e50 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -616,6 +616,9 @@
      * Removes all activities that belong to this process and finishes other containers/activities
      * configured to finish together.
      */
+    // Suppress GuardedBy warning because lint ask to mark this method as
+    // @GuardedBy(container.mController.mLock), which is mLock itself
+    @SuppressWarnings("GuardedBy")
     @GuardedBy("mController.mLock")
     void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
             @NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
index 396956e..6624c70 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
@@ -77,9 +77,11 @@
     @NonNull
     TransactionRecord startNewTransaction(@Nullable IBinder taskFragmentTransactionToken) {
         if (mCurrentTransaction != null) {
+            final TransactionRecord lastTransaction = mCurrentTransaction;
             mCurrentTransaction = null;
             throw new IllegalStateException(
-                    "The previous transaction has not been applied or aborted,");
+                    "The previous transaction:" + lastTransaction + " has not been applied or "
+                            + "aborted.");
         }
         mCurrentTransaction = new TransactionRecord(taskFragmentTransactionToken);
         return mCurrentTransaction;
@@ -199,5 +201,15 @@
                     ? mOriginType
                     : TASK_FRAGMENT_TRANSIT_CHANGE;
         }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return TransactionRecord.class.getSimpleName() + "{"
+                    + "token=" + mTaskFragmentTransactionToken
+                    + ", type=" + getTransactionTransitionType()
+                    + ", transaction=" + mTransaction
+                    + "}";
+        }
     }
 }
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 5ef6a52..bc92101 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -472,6 +472,29 @@
         verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs));
     }
 
+    @Test
+    public void testOnTaskFragmentParentInfoChanged_positionOnlyChange_earlyReturn() {
+        final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+
+        final TaskContainer taskContainer = overlayContainer.getTaskContainer();
+        spyOn(taskContainer);
+        final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
+        final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
+                new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(),
+                true /* visible */, false /* hasDirectActivity */, null /* decorSurface */);
+        parentInfo.getConfiguration().windowConfiguration.getBounds().offset(10, 10);
+
+        mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo);
+
+        // The parent info must be applied to the task container
+        verify(taskContainer).updateTaskFragmentParentInfo(parentInfo);
+        verify(mSplitController, never()).updateContainer(any(), any());
+
+        assertWithMessage("The overlay container must still be dismissed even if "
+                + "#updateContainer is not called")
+                .that(taskContainer.getOverlayContainer()).isNull();
+    }
+
     /**
      * A simplified version of {@link SplitController.ActivityStartMonitor
      * #createOrUpdateOverlayTaskFragmentIfNeeded}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index 6213f62..8f04f12 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -63,6 +63,10 @@
         if ((touchX < mStartThresholdX && mSwipeEdge == BackEvent.EDGE_LEFT)
                 || (touchX > mStartThresholdX && mSwipeEdge == BackEvent.EDGE_RIGHT)) {
             mStartThresholdX = touchX;
+            if ((mSwipeEdge == BackEvent.EDGE_LEFT && mStartThresholdX < mInitTouchX)
+                    || (mSwipeEdge == BackEvent.EDGE_RIGHT && mStartThresholdX > mInitTouchX)) {
+                mInitTouchX = mStartThresholdX;
+            }
         }
         mLatestTouchX = touchX;
         mLatestTouchY = touchY;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
index bf07dcc..6dbb1e2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
@@ -170,6 +170,71 @@
         nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / nonLinearTarget)
     }
 
+    @Test
+    fun restartingGesture_resetsInitialTouchX_leftEdge() {
+        val linearTracker = linearTouchTracker()
+        linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
+        var touchX = 100f
+        val velocityX = 0f
+        val velocityY = 0f
+
+        // assert that progress is increased when increasing touchX
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
+
+        // assert that progress is reset to 0 when start location is updated
+        linearTracker.updateStartLocation()
+        linearTracker.assertProgress(0f)
+
+        // assert that progress remains 0 when touchX is decreased
+        touchX -= 50
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress(0f)
+
+        // assert that progress uses new minimal touchX for progress calculation
+        val newInitialTouchX = touchX
+        touchX += 100
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE)
+
+        // assert the same for triggerBack==true
+        linearTracker.triggerBack = true
+        linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE)
+    }
+
+    @Test
+    fun restartingGesture_resetsInitialTouchX_rightEdge() {
+        val linearTracker = linearTouchTracker()
+        linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT)
+
+        var touchX = INITIAL_X_RIGHT_EDGE - 100f
+        val velocityX = 0f
+        val velocityY = 0f
+
+        // assert that progress is increased when decreasing touchX
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / MAX_DISTANCE)
+
+        // assert that progress is reset to 0 when start location is updated
+        linearTracker.updateStartLocation()
+        linearTracker.assertProgress(0f)
+
+        // assert that progress remains 0 when touchX is increased
+        touchX += 50
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress(0f)
+
+        // assert that progress uses new maximal touchX for progress calculation
+        val newInitialTouchX = touchX
+        touchX -= 100
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE)
+
+        // assert the same for triggerBack==true
+        linearTracker.triggerBack = true
+        linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE)
+    }
+
     companion object {
         private const val MAX_DISTANCE = 500f
         private const val LINEAR_DISTANCE = 400f
diff --git a/media/java/android/media/LoudnessCodecConfigurator.java b/media/java/android/media/LoudnessCodecConfigurator.java
index de9d87c0..aadd783 100644
--- a/media/java/android/media/LoudnessCodecConfigurator.java
+++ b/media/java/android/media/LoudnessCodecConfigurator.java
@@ -234,19 +234,21 @@
      * @param mediaCodec the codec to start receiving asynchronous loudness
      *                   updates. The codec has to be in a configured or started
      *                   state in order to add it for loudness updates.
-     * @throws IllegalArgumentException if the {@code mediaCodec} was not configured,
-     *                                  does not contain loudness metadata or if it
-     *                                  was already added before
+     * @throws IllegalArgumentException if the same {@code mediaCodec} was already
+     *                                  added before.
+     * @return {@code false} if the {@code mediaCodec} was not configured or does
+     *         not contain loudness metadata, {@code true} otherwise.
      */
     @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
-    public void addMediaCodec(@NonNull MediaCodec mediaCodec) {
+    public boolean addMediaCodec(@NonNull MediaCodec mediaCodec) {
         final MediaCodec mc = Objects.requireNonNull(mediaCodec,
                 "MediaCodec for addMediaCodec cannot be null");
         int piid = PLAYER_PIID_INVALID;
         final LoudnessCodecInfo mcInfo = getCodecInfo(mc);
 
         if (mcInfo == null) {
-            throw new IllegalArgumentException("Could not extract codec loudness information");
+            Log.v(TAG, "Could not extract codec loudness information");
+            return false;
         }
         synchronized (mConfiguratorLock) {
             final AtomicBoolean containsCodec = new AtomicBoolean(false);
@@ -271,6 +273,8 @@
         if (piid != PLAYER_PIID_INVALID) {
             mLcDispatcher.addLoudnessCodecInfo(piid, mediaCodec.hashCode(), mcInfo);
         }
+
+        return true;
     }
 
     /**
diff --git a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
index ce1004c..74e5612 100644
--- a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
+++ b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
@@ -19,6 +19,7 @@
 import static android.media.audio.Flags.FLAG_LOUDNESS_CONFIGURATOR_API;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -191,6 +192,18 @@
 
     @Test
     @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
+    public void addUnconfiguredMediaCodec_returnsFalse() throws Exception {
+        final MediaCodec mediaCodec = MediaCodec.createDecoderByType("audio/mpeg");
+
+        try {
+            assertFalse(mLcc.addMediaCodec(mediaCodec));
+        } finally {
+            mediaCodec.release();
+        }
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
     public void setClearTrack_removeAllAudioServicePiidCodecs() throws Exception {
         final ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
         final AudioTrack track = createAudioTrack();
diff --git a/packages/CredentialManager/res/drawable/more_horiz_24px.xml b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
new file mode 100644
index 0000000..7b235f8
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M240,560Q207,560 183.5,536.5Q160,513 160,480Q160,447 183.5,423.5Q207,400 240,400Q273,400 296.5,423.5Q320,447 320,480Q320,513 296.5,536.5Q273,560 240,560ZM480,560Q447,560 423.5,536.5Q400,513 400,480Q400,447 423.5,423.5Q447,400 480,400Q513,400 536.5,423.5Q560,447 560,480Q560,513 536.5,536.5Q513,560 480,560ZM720,560Q687,560 663.5,536.5Q640,513 640,480Q640,447 663.5,423.5Q687,400 720,400Q753,400 776.5,423.5Q800,447 800,480Q800,513 776.5,536.5Q753,560 720,560Z"/>
+</vector>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index dfa5735..8ac364e7 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -305,6 +305,14 @@
         var i = 0
         var datasetAdded = false
 
+        val duplicateDisplayNames: MutableMap<String, Boolean> = mutableMapOf()
+        providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach {
+            val credentialEntry = it.sortedCredentialEntryList.first()
+            credentialEntry.displayName?.let {displayName ->
+                val duplicateEntry = duplicateDisplayNames.contains(displayName)
+                duplicateDisplayNames[displayName] = duplicateEntry
+            }
+        }
         providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@{
             val primaryEntry = it.sortedCredentialEntryList.first()
             val pendingIntent = primaryEntry.pendingIntent
@@ -339,10 +347,14 @@
                 } else {
                     spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
                 }
+                val displayName : String = primaryEntry.displayName ?: primaryEntry.userName
                 val sliceBuilder = InlineSuggestionUi
                         .newContentBuilder(pendingIntent)
-                        .setTitle(primaryEntry.userName)
+                        .setTitle(displayName)
                 sliceBuilder.setStartIcon(icon)
+                if (duplicateDisplayNames[displayName] == true) {
+                    sliceBuilder.setSubtitle(primaryEntry.userName)
+                }
                 inlinePresentation = InlinePresentation(
                         sliceBuilder.build().slice, spec, /* pinned= */ false)
             }
@@ -398,7 +410,7 @@
         val sliceBuilder = InlineSuggestionUi
                 .newContentBuilder(bottomSheetPendingIntent)
                 .setStartIcon(Icon.createWithResource(this,
-                        com.android.credentialmanager.R.drawable.ic_other_sign_in_24))
+                        com.android.credentialmanager.R.drawable.more_horiz_24px))
         val presentationBuilder = Presentations.Builder()
                 .setInlinePresentation(InlinePresentation(
                         sliceBuilder.build().slice, spec, /* pinned= */ true))
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
index 754437e..b5af845 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
@@ -76,7 +76,7 @@
         boolean hasRequestInstallPermission = Arrays.asList(getRequestedPermissions(callingPackage))
                 .contains(permission.REQUEST_INSTALL_PACKAGES);
         boolean hasInstallPermission = getBaseContext().checkPermission(permission.INSTALL_PACKAGES,
-                0 /* random value for pid */, callingUid) != PackageManager.PERMISSION_GRANTED;
+                0 /* random value for pid */, callingUid) == PackageManager.PERMISSION_GRANTED;
         if (!hasRequestInstallPermission && !hasInstallPermission) {
             Log.e(TAG, "Uid " + callingUid + " does not have "
                     + permission.REQUEST_INSTALL_PACKAGES + " or "
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
index 6ccbc4c..42dd382 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
@@ -28,12 +28,13 @@
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         String appTitle = getArguments().getString(UnarchiveActivity.APP_TITLE);
+        String installerTitle = getArguments().getString(UnarchiveActivity.INSTALLER_TITLE);
 
         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
 
         dialogBuilder.setTitle(
                 String.format(getContext().getString(R.string.unarchive_application_title),
-                        appTitle));
+                        appTitle, installerTitle));
         dialogBuilder.setMessage(R.string.unarchive_body_text);
 
         dialogBuilder.setPositiveButton(R.string.restore, this);
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index f4edb36..460a6f7 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -24,7 +24,8 @@
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
 import com.android.settingslib.spa.gallery.card.CardPageProvider
 import com.android.settingslib.spa.gallery.chart.ChartPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
+import com.android.settingslib.spa.gallery.dialog.NavDialogProvider
 import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
 import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuBoxPageProvider
 import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuCheckBoxProvider
@@ -91,7 +92,8 @@
                 ProgressBarPageProvider,
                 LoadingBarPageProvider,
                 ChartPageProvider,
-                AlertDialogPageProvider,
+                DialogMainPageProvider,
+                NavDialogProvider,
                 ItemListPageProvider,
                 ItemOperatePageProvider,
                 OperateListPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
similarity index 84%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
index 1545a3e..4e3fcee 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
@@ -28,10 +28,10 @@
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 
-private const val TITLE = "AlertDialogPage"
+private const val TITLE = "Category: Dialog"
 
-object AlertDialogPageProvider : SettingsPageProvider {
-    override val name = "AlertDialogPage"
+object DialogMainPageProvider : SettingsPageProvider {
+    override val name = "DialogMain"
     private val owner = createSettingsPage()
 
     override fun buildEntry(arguments: Bundle?): List<SettingsEntry> = listOf(
@@ -47,6 +47,12 @@
                 override val onClick = alertDialogPresenter::open
             })
         }.build(),
+        SettingsEntryBuilder.create("NavDialog", owner).setUiLayoutFn {
+            Preference(object : PreferenceModel {
+                override val title = "Navigate to Dialog"
+                override val onClick = navigator(route = NavDialogProvider.name)
+            })
+        }.build(),
     )
 
     fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt
new file mode 100644
index 0000000..6f79911
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.dialog
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.widget.dialog.SettingsDialogCard
+
+object NavDialogProvider : SettingsPageProvider {
+    override val name = "NavDialog"
+    override val navType = SettingsPageProvider.NavType.Dialog
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        SettingsDialogCard("Example Nav Dialog") {}
+    }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 6a2e598..1f028d5 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -30,7 +30,7 @@
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
 import com.android.settingslib.spa.gallery.card.CardPageProvider
 import com.android.settingslib.spa.gallery.chart.ChartPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
 import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
 import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
 import com.android.settingslib.spa.gallery.page.ArgumentPageModel
@@ -71,7 +71,7 @@
             ProgressBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             LoadingBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
-            AlertDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+            DialogMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             EditorMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             CardPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             CopyablePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 9f8c868..5605485 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -22,7 +22,6 @@
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
 import androidx.annotation.VisibleForTesting
-import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
@@ -30,15 +29,19 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.core.view.WindowCompat
+import androidx.navigation.NavBackStackEntry
 import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavGraphBuilder
 import androidx.navigation.compose.NavHost
 import androidx.navigation.compose.composable
+import androidx.navigation.compose.dialog
 import androidx.navigation.compose.rememberNavController
 import com.android.settingslib.spa.R
 import com.android.settingslib.spa.framework.common.LogCategory
 import com.android.settingslib.spa.framework.common.NullPageProvider
 import com.android.settingslib.spa.framework.common.SettingsPage
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.SettingsPageProvider.NavType
 import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
@@ -127,27 +130,31 @@
     allProvider: Collection<SettingsPageProvider>,
     content: @Composable (SettingsPage) -> Unit,
 ) {
-    // TODO(b/298520326): Remove Box after the issue is fixed.
-    // Wrap the top level node into a Box to workaround an issue of Compose 1.6.0-alpha03.
-    Box {
-        NavHost(
-            navController = navController,
-            startDestination = NullPageProvider.name,
-        ) {
-            composable(NullPageProvider.name) {}
-            for (spp in allProvider) {
-                animatedComposable(
-                    route = spp.name + spp.parameter.navRoute(),
-                    arguments = spp.parameter,
-                ) { navBackStackEntry ->
-                    val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
-                    content(page)
-                }
+    NavHost(
+        navController = navController,
+        startDestination = NullPageProvider.name,
+    ) {
+        composable(NullPageProvider.name) {}
+        for (spp in allProvider) {
+            destination(spp) { navBackStackEntry ->
+                val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
+                content(page)
             }
         }
     }
 }
 
+private fun NavGraphBuilder.destination(
+    spp: SettingsPageProvider,
+    content: @Composable (NavBackStackEntry) -> Unit,
+) {
+    val route = spp.name + spp.parameter.navRoute()
+    when (spp.navType) {
+        NavType.Page -> animatedComposable(route, spp.parameter) { content(it) }
+        NavType.Dialog -> dialog(route, spp.parameter) { content(it) }
+    }
+}
+
 @Composable
 private fun NavControllerWrapperImpl.InitialDestination(
     initialIntent: Intent?,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
index 18f964e..81bee5e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
@@ -34,6 +34,14 @@
     /** The page provider name, needs to be *unique* and *stable*. */
     val name: String
 
+    enum class NavType {
+        Page,
+        Dialog,
+    }
+
+    val navType: NavType
+        get() = NavType.Page
+
     /** The display name of this page provider, for better readability. */
     val displayName: String
         get() = name
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
index 8b172da..f08e740 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
@@ -19,10 +19,13 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.AlertDialogDefaults
 import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.window.Dialog
+import androidx.navigation.compose.NavHost
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsShape
 import com.android.settingslib.spa.widget.ui.SettingsTitle
@@ -34,13 +37,27 @@
     content: @Composable () -> Unit,
 ) {
     Dialog(onDismissRequest = onDismissRequest) {
-        Card(shape = SettingsShape.CornerExtraLarge) {
-            Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) {
-                Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) {
-                    SettingsTitle(title = title, useMediumWeight = true)
-                }
-                content()
+        SettingsDialogCard(title, content)
+    }
+}
+
+/**
+ * Card for dialog, suitable for independent dialog in the [NavHost].
+ */
+@Composable
+fun SettingsDialogCard(
+    title: String,
+    content: @Composable () -> Unit,
+) {
+    Card(
+        shape = SettingsShape.CornerExtraLarge,
+        colors = CardDefaults.cardColors(containerColor = AlertDialogDefaults.containerColor),
+    ) {
+        Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) {
+            Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) {
+                SettingsTitle(title = title, useMediumWeight = true)
             }
+            content()
         }
     }
 }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
index 92d3411..8cbd964 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onAllNodesWithText
 import androidx.compose.ui.test.onNodeWithText
@@ -29,12 +30,14 @@
 import com.android.settingslib.spa.framework.common.SettingsPage
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.tests.testutils.SppDialog
 import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
 import com.android.settingslib.spa.tests.testutils.SpaLoggerForTest
 import com.android.settingslib.spa.tests.testutils.SppDisabled
 import com.android.settingslib.spa.tests.testutils.SppHome
 import com.android.settingslib.spa.testutils.waitUntil
-import com.google.common.truth.Truth
+import com.android.settingslib.spa.testutils.waitUntilExists
+import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -106,11 +109,31 @@
         composeTestRule.onNodeWithText(sppDisabled.getTitle(null)).assertDoesNotExist()
         spaLogger.verifyPageEvent(pageDisabled.id, 0, 0)
     }
+
+    @Test
+    fun browseContent_dialog() {
+        val spaEnvironment = SpaEnvironmentForTest(
+            context = context,
+            rootPages = listOf(SppHome.createSettingsPage()),
+            logger = spaLogger,
+        )
+        SpaEnvironmentFactory.reset(spaEnvironment)
+        val sppRepository by spaEnvironment.pageProviderRepository
+
+        composeTestRule.setContent {
+            BrowseContent(
+                sppRepository = sppRepository,
+                isPageEnabled = SettingsPage::isEnabled,
+                initialIntent = null,
+            )
+        }
+        composeTestRule.onNodeWithText(SppDialog.name).performClick()
+
+        composeTestRule.waitUntilExists(hasText(SppDialog.CONTENT))
+    }
 }
 
 private fun SpaLoggerForTest.verifyPageEvent(id: String, entryCount: Int, leaveCount: Int) {
-    Truth.assertThat(getEventCount(id, LogEvent.PAGE_ENTER, LogCategory.FRAMEWORK))
-        .isEqualTo(entryCount)
-    Truth.assertThat(getEventCount(id, LogEvent.PAGE_LEAVE, LogCategory.FRAMEWORK))
-        .isEqualTo(leaveCount)
+    assertThat(getEventCount(id, LogEvent.PAGE_ENTER, LogCategory.FRAMEWORK)).isEqualTo(entryCount)
+    assertThat(getEventCount(id, LogEvent.PAGE_LEAVE, LogCategory.FRAMEWORK)).isEqualTo(leaveCount)
 }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
index b139f28..0a1c05f 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
@@ -22,6 +22,7 @@
 import com.android.settingslib.spa.framework.util.genEntryId
 import com.android.settingslib.spa.framework.util.genPageId
 import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
+import com.android.settingslib.spa.tests.testutils.SppDialog
 import com.android.settingslib.spa.tests.testutils.SppHome
 import com.android.settingslib.spa.tests.testutils.SppLayer1
 import com.android.settingslib.spa.tests.testutils.SppLayer2
@@ -39,26 +40,21 @@
     @Test
     fun testGetPageWithEntry() {
         val pageWithEntry = entryRepository.getAllPageWithEntry()
-        assertThat(pageWithEntry.size).isEqualTo(3)
-        assertThat(
-            entryRepository.getPageWithEntry(genPageId("SppHome"))
-                ?.entries?.size
-        ).isEqualTo(1)
-        assertThat(
-            entryRepository.getPageWithEntry(genPageId("SppLayer1"))
-                ?.entries?.size
-        ).isEqualTo(3)
-        assertThat(
-            entryRepository.getPageWithEntry(genPageId("SppLayer2"))
-                ?.entries?.size
-        ).isEqualTo(2)
+
+        assertThat(pageWithEntry).hasSize(4)
+        assertThat(entryRepository.getPageWithEntry(genPageId("SppHome"))?.entries)
+            .hasSize(2)
+        assertThat(entryRepository.getPageWithEntry(genPageId("SppLayer1"))?.entries)
+            .hasSize(3)
+        assertThat(entryRepository.getPageWithEntry(genPageId("SppLayer2"))?.entries)
+            .hasSize(2)
         assertThat(entryRepository.getPageWithEntry(genPageId("SppWithParam"))).isNull()
     }
 
     @Test
     fun testGetEntry() {
         val entry = entryRepository.getAllEntries()
-        assertThat(entry.size).isEqualTo(7)
+        assertThat(entry).hasSize(8)
         assertThat(
             entryRepository.getEntry(
                 genEntryId(
@@ -91,6 +87,16 @@
         ).isNotNull()
         assertThat(
             entryRepository.getEntry(
+                genEntryId(
+                    "INJECT",
+                    SppDialog.createSettingsPage(),
+                    SppHome.createSettingsPage(),
+                    SppDialog.createSettingsPage(),
+                )
+            )
+        ).isNotNull()
+        assertThat(
+            entryRepository.getEntry(
                 genEntryId("Layer1Entry1", SppLayer1.createSettingsPage())
             )
         ).isNotNull()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
index 8576573..169c541 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
@@ -25,39 +25,55 @@
 @RunWith(AndroidJUnit4::class)
 class SettingsPageProviderRepositoryTest {
     @Test
-    fun getStartPageTest() {
-        val sppRepoEmpty = SettingsPageProviderRepository(emptyList(), emptyList())
+    fun rootPages_empty() {
+        val sppRepoEmpty = SettingsPageProviderRepository(emptyList())
+
         assertThat(sppRepoEmpty.getDefaultStartPage()).isEqualTo("")
         assertThat(sppRepoEmpty.getAllRootPages()).isEmpty()
-
-        val nullPage = NullPageProvider.createSettingsPage()
-        val sppRepoNull =
-            SettingsPageProviderRepository(emptyList(), listOf(nullPage))
-        assertThat(sppRepoNull.getDefaultStartPage()).isEqualTo("NULL")
-        assertThat(sppRepoNull.getAllRootPages()).contains(nullPage)
-
-        val rootPage1 = createSettingsPage(sppName = "Spp1", displayName = "Spp1")
-        val rootPage2 = createSettingsPage(sppName = "Spp2", displayName = "Spp2")
-        val sppRepo = SettingsPageProviderRepository(emptyList(), listOf(rootPage1, rootPage2))
-        val allRoots = sppRepo.getAllRootPages()
-        assertThat(sppRepo.getDefaultStartPage()).isEqualTo("Spp1")
-        assertThat(allRoots.size).isEqualTo(2)
-        assertThat(allRoots).contains(rootPage1)
-        assertThat(allRoots).contains(rootPage2)
     }
 
     @Test
-    fun getProviderTest() {
-        val sppRepoEmpty = SettingsPageProviderRepository(emptyList(), emptyList())
+    fun rootPages_single() {
+        val nullPage = NullPageProvider.createSettingsPage()
+
+        val sppRepoNull = SettingsPageProviderRepository(
+            allPageProviders = emptyList(),
+            rootPages = listOf(nullPage),
+        )
+
+        assertThat(sppRepoNull.getDefaultStartPage()).isEqualTo("NULL")
+        assertThat(sppRepoNull.getAllRootPages()).containsExactly(nullPage)
+    }
+
+    @Test
+    fun rootPages_twoPages() {
+        val rootPage1 = createSettingsPage(sppName = "Spp1", displayName = "Spp1")
+        val rootPage2 = createSettingsPage(sppName = "Spp2", displayName = "Spp2")
+
+        val sppRepo = SettingsPageProviderRepository(
+            allPageProviders = emptyList(),
+            rootPages = listOf(rootPage1, rootPage2),
+        )
+
+        assertThat(sppRepo.getDefaultStartPage()).isEqualTo("Spp1")
+        assertThat(sppRepo.getAllRootPages()).containsExactly(rootPage1, rootPage2)
+    }
+
+    @Test
+    fun getProviderOrNull_empty() {
+        val sppRepoEmpty = SettingsPageProviderRepository(emptyList())
         assertThat(sppRepoEmpty.getAllProviders()).isEmpty()
         assertThat(sppRepoEmpty.getProviderOrNull("Spp")).isNull()
+    }
 
+    @Test
+    fun getProviderOrNull_single() {
         val sppRepo = SettingsPageProviderRepository(listOf(
             object : SettingsPageProvider {
                 override val name = "Spp"
             }
-        ), emptyList())
-        assertThat(sppRepo.getAllProviders().size).isEqualTo(1)
+        ))
+        assertThat(sppRepo.getAllProviders()).hasSize(1)
         assertThat(sppRepo.getProviderOrNull("Spp")).isNotNull()
         assertThat(sppRepo.getProviderOrNull("SppUnknown")).isNull()
     }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
index 2755b4e..22a5ca3 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
@@ -21,6 +21,8 @@
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
 import androidx.navigation.NavType
 import androidx.navigation.navArgument
 import com.android.settingslib.spa.framework.BrowseActivity
@@ -88,6 +90,7 @@
         val owner = this.createSettingsPage()
         return listOf(
             SppLayer1.buildInject().setLink(fromPage = owner).build(),
+            SppDialog.buildInject().setLink(fromPage = owner).build(),
         )
     }
 }
@@ -160,6 +163,21 @@
     }
 }
 
+object SppDialog : SettingsPageProvider {
+    override val name = "SppDialog"
+    override val navType = SettingsPageProvider.NavType.Dialog
+
+    const val CONTENT = "SppDialog Content"
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        Text(CONTENT)
+    }
+
+    fun buildInject() = SettingsEntryBuilder.createInject(this.createSettingsPage())
+        .setMacro { SimplePreferenceMacro(title = name, clickRoute = name) }
+}
+
 object SppForSearch : SettingsPageProvider {
     override val name = "SppForSearch"
 
@@ -223,6 +241,7 @@
                         navArgument("rt_param") { type = NavType.StringType },
                     )
                 },
+                SppDialog,
             ),
             rootPages
         )
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 9560b8d..cdb8740 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -19,6 +19,7 @@
 
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
@@ -42,18 +43,16 @@
     BluetoothMediaDevice(
             Context context,
             CachedBluetoothDevice device,
-            MediaRoute2Info info,
-            String packageName) {
-        this(context, device, info, packageName, null);
+            MediaRoute2Info info) {
+        this(context, device, info, null);
     }
 
     BluetoothMediaDevice(
             Context context,
             CachedBluetoothDevice device,
             MediaRoute2Info info,
-            String packageName,
             RouteListingPreference.Item item) {
-        super(context, info, packageName, item);
+        super(context, info, item);
         mCachedDevice = device;
         mAudioManager = context.getSystemService(AudioManager.class);
         initDeviceRecord();
@@ -100,7 +99,12 @@
 
     @Override
     public String getId() {
-        return MediaDeviceUtils.getId(mCachedDevice);
+        if (mCachedDevice.isHearingAidDevice()) {
+            if (mCachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
+                return Long.toString(mCachedDevice.getHiSyncId());
+            }
+        }
+        return mCachedDevice.getAddress();
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
index 4e0ebd1..338fb87 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
@@ -34,9 +34,8 @@
     ComplexMediaDevice(
             Context context,
             MediaRoute2Info info,
-            String packageName,
             RouteListingPreference.Item item) {
-        super(context, info, packageName, item);
+        super(context, info, item);
     }
 
     // MediaRoute2Info.getName was made public on API 34, but exists since API 30.
@@ -63,7 +62,7 @@
 
     @Override
     public String getId() {
-        return MediaDeviceUtils.getId(mRouteInfo);
+        return mRouteInfo.getId();
     }
 
     public boolean isConnected() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 012cbc0..1347dd1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -45,14 +45,13 @@
     InfoMediaDevice(
             Context context,
             MediaRoute2Info info,
-            String packageName,
             RouteListingPreference.Item item) {
-        super(context, info, packageName, item);
+        super(context, info, item);
         initDeviceRecord();
     }
 
-    InfoMediaDevice(Context context, MediaRoute2Info info, String packageName) {
-        this(context, info, packageName, null);
+    InfoMediaDevice(Context context, MediaRoute2Info info) {
+        this(context, info, null);
     }
 
     @Override
@@ -118,7 +117,7 @@
 
     @Override
     public String getId() {
-        return MediaDeviceUtils.getId(mRouteInfo);
+        return mRouteInfo.getId();
     }
 
     public boolean isConnected() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index e5fce5b..581c7de 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -383,7 +383,7 @@
         for (MediaRoute2Info route : getSelectableRoutes(info)) {
             deviceList.add(
                     new InfoMediaDevice(
-                            mContext, route, mPackageName, mPreferenceItemMap.get(route.getId())));
+                            mContext, route, mPreferenceItemMap.get(route.getId())));
         }
         return deviceList;
     }
@@ -410,7 +410,7 @@
         for (MediaRoute2Info route : getDeselectableRoutes(info)) {
             deviceList.add(
                     new InfoMediaDevice(
-                            mContext, route, mPackageName, mPreferenceItemMap.get(route.getId())));
+                            mContext, route, mPreferenceItemMap.get(route.getId())));
             Log.d(TAG, route.getName() + " is deselectable for " + mPackageName);
         }
         return deviceList;
@@ -434,7 +434,7 @@
         for (MediaRoute2Info route : getSelectedRoutes(info)) {
             deviceList.add(
                     new InfoMediaDevice(
-                            mContext, route, mPackageName, mPreferenceItemMap.get(route.getId())));
+                            mContext, route, mPreferenceItemMap.get(route.getId())));
         }
         return deviceList;
     }
@@ -633,7 +633,6 @@
                         new InfoMediaDevice(
                                 mContext,
                                 route,
-                                mPackageName,
                                 mPreferenceItemMap.get(route.getId()));
                 break;
             case TYPE_BUILTIN_SPEAKER:
@@ -650,7 +649,6 @@
                         new PhoneMediaDevice(
                                 mContext,
                                 route,
-                                mPackageName,
                                 mPreferenceItemMap.getOrDefault(route.getId(), null));
                 break;
             case TYPE_HEARING_AID:
@@ -666,7 +664,6 @@
                                     mContext,
                                     cachedDevice,
                                     route,
-                                    mPackageName,
                                     mPreferenceItemMap.getOrDefault(route.getId(), null));
                 }
                 break;
@@ -675,7 +672,6 @@
                         new ComplexMediaDevice(
                                 mContext,
                                 route,
-                                mPackageName,
                                 mPreferenceItemMap.get(route.getId()));
                 break;
             default:
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index dbc3bf7..ebcca42 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -567,7 +567,7 @@
                 final CachedBluetoothDevice cachedDevice =
                         cachedDeviceManager.findDevice(device);
                 if (isBondedMediaDevice(cachedDevice) && isMutingExpectedDevice(cachedDevice)) {
-                    return new BluetoothMediaDevice(mContext, cachedDevice, null, mPackageName);
+                    return new BluetoothMediaDevice(mContext, cachedDevice, null);
                 }
             }
             return null;
@@ -614,7 +614,7 @@
             mDisconnectedMediaDevices.clear();
             for (CachedBluetoothDevice cachedDevice : cachedBluetoothDeviceList) {
                 final MediaDevice mediaDevice =
-                        new BluetoothMediaDevice(mContext, cachedDevice, null, mPackageName);
+                        new BluetoothMediaDevice(mContext, cachedDevice, null);
                 if (!mMediaDevices.contains(mediaDevice)) {
                     cachedDevice.registerCallback(mDeviceAttributeChangeCallback);
                     mDisconnectedMediaDevices.add(mediaDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index c8e4c0c..f2d9d14 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -121,16 +121,13 @@
     protected final Context mContext;
     protected final MediaRoute2Info mRouteInfo;
     protected final RouteListingPreference.Item mItem;
-    protected final String mPackageName;
 
     MediaDevice(
             Context context,
             MediaRoute2Info info,
-            String packageName,
             RouteListingPreference.Item item) {
         mContext = context;
         mRouteInfo = info;
-        mPackageName = packageName;
         mItem = item;
         setType(info);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
deleted file mode 100644
index b3a52b9..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.media;
-
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHearingAid;
-import android.media.MediaRoute2Info;
-
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-
-/**
- * MediaDeviceUtils provides utility function for MediaDevice
- */
-public class MediaDeviceUtils {
-    /**
-     * Use CachedBluetoothDevice address to represent unique id
-     *
-     * @param cachedDevice the CachedBluetoothDevice
-     * @return CachedBluetoothDevice address
-     */
-    public static String getId(CachedBluetoothDevice cachedDevice) {
-        if (cachedDevice.isHearingAidDevice()) {
-            if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
-                return Long.toString(cachedDevice.getHiSyncId());
-            }
-        }
-        return cachedDevice.getAddress();
-    }
-
-    /**
-     * Use BluetoothDevice address to represent unique id
-     *
-     * @param bluetoothDevice the BluetoothDevice
-     * @return BluetoothDevice address
-     */
-    public static String getId(BluetoothDevice bluetoothDevice) {
-        return bluetoothDevice.getAddress();
-    }
-
-    /**
-     * Use MediaRoute2Info id to represent unique id
-     *
-     * @param route the MediaRoute2Info
-     * @return MediaRoute2Info id
-     */
-    public static String getId(MediaRoute2Info route) {
-        return route.getId();
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 0676ce5..d6f1eab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -117,16 +117,15 @@
         return name.toString();
     }
 
-    PhoneMediaDevice(Context context, MediaRoute2Info info, String packageName) {
-        this(context, info, packageName, null);
+    PhoneMediaDevice(Context context, MediaRoute2Info info) {
+        this(context, info, null);
     }
 
     PhoneMediaDevice(
             Context context,
             MediaRoute2Info info,
-            String packageName,
             RouteListingPreference.Item item) {
-        super(context, info, packageName, item);
+        super(context, info, item);
         mDeviceIconUtil = new DeviceIconUtil(mContext);
         initDeviceRecord();
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
index f50802a..7061742 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
@@ -39,6 +39,8 @@
 @RunWith(RobolectricTestRunner.class)
 public class BluetoothMediaDeviceTest {
 
+    private static final String TEST_ADDRESS = "11:22:33:44:55:66";
+
     @Mock
     private CachedBluetoothDevice mDevice;
 
@@ -54,7 +56,7 @@
         when(mDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
         when(mDevice.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true);
 
-        mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null, null);
+        mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null);
     }
 
     @Test
@@ -111,4 +113,10 @@
 
         assertThat(mBluetoothMediaDevice.getIcon() instanceof BitmapDrawable).isFalse();
     }
+
+    @Test
+    public void getId_returnsCachedBluetoothDeviceAddress() {
+        when(mDevice.getAddress()).thenReturn(TEST_ADDRESS);
+        assertThat(mBluetoothMediaDevice.getId()).isEqualTo(TEST_ADDRESS);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index a072c17..0665308 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -65,7 +65,7 @@
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
 
-        mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo, TEST_PACKAGE_NAME);
+        mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 2252b69..f0330c4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -547,7 +547,7 @@
     @Test
     public void connectDeviceWithoutPackageName_noSession_returnFalse() {
         final MediaRoute2Info info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, info, TEST_PACKAGE_NAME);
+        final MediaDevice device = new InfoMediaDevice(mContext, info);
 
         final List<RoutingSessionInfo> infos = new ArrayList<>();
 
@@ -623,7 +623,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
 
         final List<String> list = new ArrayList<>();
         list.add(TEST_ID);
@@ -644,7 +644,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
 
         final List<String> list = new ArrayList<>();
         list.add("fake_id");
@@ -674,7 +674,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
 
         final List<String> list = new ArrayList<>();
         list.add(TEST_ID);
@@ -695,7 +695,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
 
         final List<String> list = new ArrayList<>();
         list.add("fake_id");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 926b41a..999e8d5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -116,8 +116,8 @@
         when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
         when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
 
-        mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1, TEST_PACKAGE_NAME));
-        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, TEST_PACKAGE_NAME);
+        mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1));
+        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
         mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
                 mInfoMediaManager, "com.test.packagename");
         mLocalMediaManager.mAudioManager = mAudioManager;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 18055d9..098ab16 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -171,17 +171,17 @@
 
         mBluetoothMediaDevice1 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice1, mBluetoothRouteInfo1, TEST_PACKAGE_NAME);
+                        mContext, mCachedDevice1, mBluetoothRouteInfo1);
         mBluetoothMediaDevice2 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice2, mBluetoothRouteInfo2, TEST_PACKAGE_NAME);
+                        mContext, mCachedDevice2, mBluetoothRouteInfo2);
         mBluetoothMediaDevice3 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice3, mBluetoothRouteInfo3, TEST_PACKAGE_NAME);
-        mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1, TEST_PACKAGE_NAME);
-        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, TEST_PACKAGE_NAME);
-        mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3, TEST_PACKAGE_NAME);
-        mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo, TEST_PACKAGE_NAME);
+                        mContext, mCachedDevice3, mBluetoothRouteInfo3);
+        mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1);
+        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
+        mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3);
+        mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo);
     }
 
     @Test
@@ -316,7 +316,7 @@
         when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
 
         final PhoneMediaDevice phoneMediaDevice =
-                new PhoneMediaDevice(mContext, phoneRouteInfo, TEST_PACKAGE_NAME);
+                new PhoneMediaDevice(mContext, phoneRouteInfo);
 
         mMediaDevices.add(mBluetoothMediaDevice1);
         mMediaDevices.add(phoneMediaDevice);
@@ -332,7 +332,7 @@
         when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
 
         final PhoneMediaDevice phoneMediaDevice =
-                new PhoneMediaDevice(mContext, phoneRouteInfo, TEST_PACKAGE_NAME);
+                new PhoneMediaDevice(mContext, phoneRouteInfo);
 
         mMediaDevices.add(mInfoMediaDevice1);
         mMediaDevices.add(phoneMediaDevice);
@@ -483,7 +483,7 @@
     public void getFeatures_noRouteInfo_returnEmptyList() {
         mBluetoothMediaDevice1 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice1, null /* MediaRoute2Info */, TEST_PACKAGE_NAME);
+                        mContext, mCachedDevice1, /* MediaRoute2Info */ null);
 
         assertThat(mBluetoothMediaDevice1.getFeatures().size()).isEqualTo(0);
     }
@@ -498,10 +498,9 @@
                         mContext,
                         mCachedDevice1,
                         null /* MediaRoute2Info */,
-                        TEST_PACKAGE_NAME,
                         mItem);
         mPhoneMediaDevice =
-                new PhoneMediaDevice(mContext, mPhoneRouteInfo, TEST_PACKAGE_NAME, mItem);
+                new PhoneMediaDevice(mContext, mPhoneRouteInfo, mItem);
 
         assertThat(mBluetoothMediaDevice1.getSelectionBehavior()).isEqualTo(
                 SELECTION_BEHAVIOR_TRANSFER);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
deleted file mode 100644
index 30a6ad2..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.media;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.bluetooth.BluetoothDevice;
-import android.media.MediaRoute2Info;
-
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-public class MediaDeviceUtilsTest {
-
-    private static final String TEST_ADDRESS = "11:22:33:44:55:66";
-    private static final String TEST_ROUTE_ID = "test_route_id";
-
-    @Mock
-    private CachedBluetoothDevice mCachedDevice;
-    @Mock
-    private BluetoothDevice mBluetoothDevice;
-    @Mock
-    private MediaRoute2Info mRouteInfo;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void getId_returnCachedBluetoothDeviceAddress() {
-        when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
-
-        final String id = MediaDeviceUtils.getId(mCachedDevice);
-
-        assertThat(id).isEqualTo(TEST_ADDRESS);
-    }
-
-    @Test
-    public void getId_returnBluetoothDeviceAddress() {
-        when(mBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS);
-
-        final String id = MediaDeviceUtils.getId(mBluetoothDevice);
-
-        assertThat(id).isEqualTo(TEST_ADDRESS);
-    }
-
-    @Test
-    public void getId_returnRouteInfoId() {
-        when(mRouteInfo.getId()).thenReturn(TEST_ROUTE_ID);
-
-        final String id = MediaDeviceUtils.getId(mRouteInfo);
-
-        assertThat(id).isEqualTo(TEST_ROUTE_ID);
-    }
-}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index d6e8d26..2e39adc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -69,6 +69,7 @@
         Settings.Global.PRIVATE_DNS_SPECIFIER,
         Settings.Global.SOFT_AP_TIMEOUT_ENABLED,
         Settings.Global.ZEN_DURATION,
+        Settings.Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE,
         Settings.Global.REVERSE_CHARGING_AUTO_ON,
         Settings.Global.CHARGING_VIBRATION_ENABLED,
         Settings.Global.AWARE_ALLOWED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 59c3cd3..e7d7bb0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -19,93 +19,105 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.provider.Settings;
 
+import com.android.server.display.feature.flags.Flags;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /** Information about the system settings to back up */
 public class SystemSettings {
 
     /**
-     * Settings to backup.
+     * Settings to back up.
      *
      * NOTE: Settings are backed up and restored in the order they appear
      *       in this array. If you have one setting depending on another,
      *       make sure that they are ordered appropriately.
      */
     @UnsupportedAppUsage
-    public static final String[] SETTINGS_TO_BACKUP = {
-        Settings.System.STAY_ON_WHILE_PLUGGED_IN,   // moved to global
-        Settings.System.WIFI_USE_STATIC_IP,
-        Settings.System.WIFI_STATIC_IP,
-        Settings.System.WIFI_STATIC_GATEWAY,
-        Settings.System.WIFI_STATIC_NETMASK,
-        Settings.System.WIFI_STATIC_DNS1,
-        Settings.System.WIFI_STATIC_DNS2,
-        Settings.System.BLUETOOTH_DISCOVERABILITY,
-        Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
-        Settings.System.FONT_SCALE,
-        Settings.System.DIM_SCREEN,
-        Settings.System.SCREEN_OFF_TIMEOUT,
-        Settings.System.SCREEN_BRIGHTNESS_MODE,
-        Settings.System.ADAPTIVE_SLEEP,             // moved to secure
-        Settings.System.APPLY_RAMPING_RINGER,
-        Settings.System.VIBRATE_INPUT_DEVICES,
-        Settings.System.MODE_RINGER_STREAMS_AFFECTED,
-        Settings.System.TEXT_AUTO_REPLACE,
-        Settings.System.TEXT_AUTO_CAPS,
-        Settings.System.TEXT_AUTO_PUNCTUATE,
-        Settings.System.TEXT_SHOW_PASSWORD,
-        Settings.System.AUTO_TIME,                  // moved to global
-        Settings.System.AUTO_TIME_ZONE,             // moved to global
-        Settings.System.TIME_12_24,
-        Settings.System.DTMF_TONE_WHEN_DIALING,
-        Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
-        Settings.System.HEARING_AID,
-        Settings.System.TTY_MODE,
-        Settings.System.MASTER_MONO,
-        Settings.System.MASTER_BALANCE,
-        Settings.System.FOLD_LOCK_BEHAVIOR,
-        Settings.System.SOUND_EFFECTS_ENABLED,
-        Settings.System.HAPTIC_FEEDBACK_ENABLED,
-        Settings.System.POWER_SOUNDS_ENABLED,       // moved to global
-        Settings.System.DOCK_SOUNDS_ENABLED,        // moved to global
-        Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
-        Settings.System.SHOW_WEB_SUGGESTIONS,
-        Settings.System.SIP_CALL_OPTIONS,
-        Settings.System.SIP_RECEIVE_CALLS,
-        Settings.System.POINTER_SPEED,
-        Settings.System.VIBRATE_ON,
-        Settings.System.VIBRATE_WHEN_RINGING,
-        Settings.System.RINGTONE,
-        Settings.System.LOCK_TO_APP_ENABLED,
-        Settings.System.NOTIFICATION_SOUND,
-        Settings.System.ACCELEROMETER_ROTATION,
-        Settings.System.SHOW_BATTERY_PERCENT,
-        Settings.System.ALARM_VIBRATION_INTENSITY,
-        Settings.System.MEDIA_VIBRATION_INTENSITY,
-        Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-        Settings.System.RING_VIBRATION_INTENSITY,
-        Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-        Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
-        Settings.System.KEYBOARD_VIBRATION_ENABLED,
-        Settings.System.HAPTIC_FEEDBACK_ENABLED,
-        Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
-        Settings.System.DISPLAY_COLOR_MODE,
-        Settings.System.ALARM_ALERT,
-        Settings.System.NOTIFICATION_LIGHT_PULSE,
-        Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
-        Settings.System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF,
-        Settings.System.UNREAD_NOTIFICATION_DOT_INDICATOR,
-        Settings.System.AUTO_LAUNCH_MEDIA_CONTROLS,
-        Settings.System.LOCALE_PREFERENCES,
-        Settings.System.TOUCHPAD_POINTER_SPEED,
-        Settings.System.TOUCHPAD_NATURAL_SCROLLING,
-        Settings.System.TOUCHPAD_TAP_TO_CLICK,
-        Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE,
-        Settings.System.CAMERA_FLASH_NOTIFICATION,
-        Settings.System.SCREEN_FLASH_NOTIFICATION,
-        Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
-        Settings.System.PEAK_REFRESH_RATE,
-        Settings.System.MIN_REFRESH_RATE,
-        Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
-        Settings.System.NOTIFICATION_COOLDOWN_ALL,
-        Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
-    };
+    public static final String[] SETTINGS_TO_BACKUP = getSettingsToBackUp();
+
+    private static String[] getSettingsToBackUp() {
+        List<String> settings = new ArrayList<>(List.of(
+                Settings.System.STAY_ON_WHILE_PLUGGED_IN,   // moved to global
+                Settings.System.WIFI_USE_STATIC_IP,
+                Settings.System.WIFI_STATIC_IP,
+                Settings.System.WIFI_STATIC_GATEWAY,
+                Settings.System.WIFI_STATIC_NETMASK,
+                Settings.System.WIFI_STATIC_DNS1,
+                Settings.System.WIFI_STATIC_DNS2,
+                Settings.System.BLUETOOTH_DISCOVERABILITY,
+                Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
+                Settings.System.FONT_SCALE,
+                Settings.System.DIM_SCREEN,
+                Settings.System.SCREEN_OFF_TIMEOUT,
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.ADAPTIVE_SLEEP,             // moved to secure
+                Settings.System.APPLY_RAMPING_RINGER,
+                Settings.System.VIBRATE_INPUT_DEVICES,
+                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+                Settings.System.TEXT_AUTO_REPLACE,
+                Settings.System.TEXT_AUTO_CAPS,
+                Settings.System.TEXT_AUTO_PUNCTUATE,
+                Settings.System.TEXT_SHOW_PASSWORD,
+                Settings.System.AUTO_TIME,                  // moved to global
+                Settings.System.AUTO_TIME_ZONE,             // moved to global
+                Settings.System.TIME_12_24,
+                Settings.System.DTMF_TONE_WHEN_DIALING,
+                Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
+                Settings.System.HEARING_AID,
+                Settings.System.TTY_MODE,
+                Settings.System.MASTER_MONO,
+                Settings.System.MASTER_BALANCE,
+                Settings.System.FOLD_LOCK_BEHAVIOR,
+                Settings.System.SOUND_EFFECTS_ENABLED,
+                Settings.System.HAPTIC_FEEDBACK_ENABLED,
+                Settings.System.POWER_SOUNDS_ENABLED,       // moved to global
+                Settings.System.DOCK_SOUNDS_ENABLED,        // moved to global
+                Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
+                Settings.System.SHOW_WEB_SUGGESTIONS,
+                Settings.System.SIP_CALL_OPTIONS,
+                Settings.System.SIP_RECEIVE_CALLS,
+                Settings.System.POINTER_SPEED,
+                Settings.System.VIBRATE_ON,
+                Settings.System.VIBRATE_WHEN_RINGING,
+                Settings.System.RINGTONE,
+                Settings.System.LOCK_TO_APP_ENABLED,
+                Settings.System.NOTIFICATION_SOUND,
+                Settings.System.ACCELEROMETER_ROTATION,
+                Settings.System.SHOW_BATTERY_PERCENT,
+                Settings.System.ALARM_VIBRATION_INTENSITY,
+                Settings.System.MEDIA_VIBRATION_INTENSITY,
+                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                Settings.System.RING_VIBRATION_INTENSITY,
+                Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+                Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
+                Settings.System.KEYBOARD_VIBRATION_ENABLED,
+                Settings.System.HAPTIC_FEEDBACK_ENABLED,
+                Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
+                Settings.System.DISPLAY_COLOR_MODE,
+                Settings.System.ALARM_ALERT,
+                Settings.System.NOTIFICATION_LIGHT_PULSE,
+                Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
+                Settings.System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF,
+                Settings.System.UNREAD_NOTIFICATION_DOT_INDICATOR,
+                Settings.System.AUTO_LAUNCH_MEDIA_CONTROLS,
+                Settings.System.LOCALE_PREFERENCES,
+                Settings.System.TOUCHPAD_POINTER_SPEED,
+                Settings.System.TOUCHPAD_NATURAL_SCROLLING,
+                Settings.System.TOUCHPAD_TAP_TO_CLICK,
+                Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE,
+                Settings.System.CAMERA_FLASH_NOTIFICATION,
+                Settings.System.SCREEN_FLASH_NOTIFICATION,
+                Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+                Settings.System.NOTIFICATION_COOLDOWN_ALL,
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED
+        ));
+        if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
+            settings.add(Settings.System.PEAK_REFRESH_RATE);
+            settings.add(Settings.System.MIN_REFRESH_RATE);
+        }
+        return settings.toArray(new String[0]);
+    }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index f8bdcf6..5022395 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -210,6 +210,8 @@
         VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, NONE_NEGATIVE_LONG_VALIDATOR);
         VALIDATORS.put(Global.STYLUS_EVER_USED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE, BOOLEAN_VALIDATOR);
 
         VALIDATORS.put(Global.Wearable.HAS_PAY_TOKENS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 6ad10cc..1481d97 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -33,6 +33,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.display.feature.flags.Flags;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -53,59 +55,6 @@
     public static final String HYBRID_SYSUI_BATTERY_WARNING_FLAGS =
             "hybrid_sysui_battery_warning_flags";
 
-    /**
-     * The following denylists contain settings that should *not* be backed up and restored to
-     * another device.  As a general rule, anything that is not user configurable should be
-     * denied (and conversely, things that *are* user configurable *should* be backed up)
-     */
-    private static final Set<String> BACKUP_DENY_LIST_SYSTEM_SETTINGS =
-            newHashSet(
-                    Settings.System.ADVANCED_SETTINGS, // candidate for backup?
-                    Settings.System.ALARM_ALERT_CACHE, // internal cache
-                    Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2
-                    Settings.System.EGG_MODE, // I am the lolrus
-                    Settings.System.END_BUTTON_BEHAVIOR, // bug?
-                    Settings.System
-                            .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, // candidate for backup?
-                    Settings.System.LOCKSCREEN_DISABLED, // ?
-                    Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup?
-                    Settings.System.MUTE_STREAMS_AFFECTED, //  candidate for backup?
-                    Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
-                    Settings.System.POINTER_LOCATION, // backup candidate?
-                    Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING, // used for testing only
-                    Settings.System.RINGTONE_CACHE, // internal cache
-                    Settings.System.SCREEN_BRIGHTNESS, // removed in P
-                    Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW
-                    Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
-                    Settings.System.SHOW_TOUCHES,
-                    Settings.System.SHOW_KEY_PRESSES,
-                    Settings.System.SHOW_ROTARY_INPUT,
-                    Settings.System.SIP_ADDRESS_ONLY, // value, not a setting
-                    Settings.System.SIP_ALWAYS, // value, not a setting
-                    Settings.System.SYSTEM_LOCALES, // bug?
-                    Settings.System.USER_ROTATION, // backup candidate?
-                    Settings.System.VIBRATE_IN_SILENT, // deprecated?
-                    Settings.System.VOLUME_ACCESSIBILITY, // used internally, changing value will
-                                                          // not change volume
-                    Settings.System.VOLUME_ALARM, // deprecated since API 2?
-                    Settings.System.VOLUME_ASSISTANT, // candidate for backup?
-                    Settings.System.VOLUME_BLUETOOTH_SCO, // deprecated since API 2?
-                    Settings.System.VOLUME_MASTER, // candidate for backup?
-                    Settings.System.VOLUME_MUSIC, // deprecated since API 2?
-                    Settings.System.VOLUME_NOTIFICATION, // deprecated since API 2?
-                    Settings.System.VOLUME_RING, // deprecated since API 2?
-                    Settings.System.VOLUME_SYSTEM, // deprecated since API 2?
-                    Settings.System.VOLUME_VOICE, // deprecated since API 2?
-                    Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
-                    Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
-                    Settings.System.SCREEN_BRIGHTNESS_FLOAT,
-                    Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
-                    Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
-                    Settings.System.WEAR_TTS_PREWARM_ENABLED,
-                    Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
-                    Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
-                    );
-
     private static final Set<String> BACKUP_DENY_LIST_GLOBAL_SETTINGS =
             newHashSet(
                     Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
@@ -737,6 +686,7 @@
                  Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
                  Settings.Secure.CONTENT_CAPTURE_ENABLED,
                  Settings.Secure.DEFAULT_INPUT_METHOD,
+                 Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD,
                  Settings.Secure.DEVICE_PAIRED,
                  Settings.Secure.DIALER_DEFAULT_APPLICATION,
                  Settings.Secure.DISABLED_PRINT_SERVICES,
@@ -862,7 +812,7 @@
         checkSettingsBackedUpOrDenied(
                 getCandidateSettings(Settings.System.class),
                 newHashSet(SystemSettings.SETTINGS_TO_BACKUP),
-                BACKUP_DENY_LIST_SYSTEM_SETTINGS);
+                getBackUpDenyListSystemSettings());
     }
 
     @Test
@@ -937,6 +887,69 @@
         checkSettingsBackedUpOrDenied(allSettings, keys, BACKUP_DENY_LIST_SECURE_SETTINGS);
     }
 
+    /**
+     * The following denylists contain settings that should *not* be backed up and restored to
+     * another device.  As a general rule, anything that is not user configurable should be
+     * denied (and conversely, things that *are* user configurable *should* be backed up)
+     */
+    private static Set<String> getBackUpDenyListSystemSettings() {
+        Set<String> settings =
+                newHashSet(
+                        Settings.System.ADVANCED_SETTINGS, // candidate for backup?
+                        Settings.System.ALARM_ALERT_CACHE, // internal cache
+                        Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2
+                        Settings.System.EGG_MODE, // I am the lolrus
+                        Settings.System.END_BUTTON_BEHAVIOR, // bug?
+                        Settings.System
+                                .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
+                        // candidate for backup?
+                        Settings.System.LOCKSCREEN_DISABLED, // ?
+                        Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup?
+                        Settings.System.MUTE_STREAMS_AFFECTED, //  candidate for backup?
+                        Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
+                        Settings.System.POINTER_LOCATION, // backup candidate?
+                        Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING,
+                        // used for testing only
+                        Settings.System.RINGTONE_CACHE, // internal cache
+                        Settings.System.SCREEN_BRIGHTNESS, // removed in P
+                        Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW
+                        Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
+                        Settings.System.SHOW_TOUCHES,
+                        Settings.System.SHOW_KEY_PRESSES,
+                        Settings.System.SHOW_ROTARY_INPUT,
+                        Settings.System.SIP_ADDRESS_ONLY, // value, not a setting
+                        Settings.System.SIP_ALWAYS, // value, not a setting
+                        Settings.System.SYSTEM_LOCALES, // bug?
+                        Settings.System.USER_ROTATION, // backup candidate?
+                        Settings.System.VIBRATE_IN_SILENT, // deprecated?
+                        Settings.System.VOLUME_ACCESSIBILITY,
+                        // used internally, changing value will
+                        // not change volume
+                        Settings.System.VOLUME_ALARM, // deprecated since API 2?
+                        Settings.System.VOLUME_ASSISTANT, // candidate for backup?
+                        Settings.System.VOLUME_BLUETOOTH_SCO, // deprecated since API 2?
+                        Settings.System.VOLUME_MASTER, // candidate for backup?
+                        Settings.System.VOLUME_MUSIC, // deprecated since API 2?
+                        Settings.System.VOLUME_NOTIFICATION, // deprecated since API 2?
+                        Settings.System.VOLUME_RING, // deprecated since API 2?
+                        Settings.System.VOLUME_SYSTEM, // deprecated since API 2?
+                        Settings.System.VOLUME_VOICE, // deprecated since API 2?
+                        Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
+                        Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
+                        Settings.System.SCREEN_BRIGHTNESS_FLOAT,
+                        Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+                        Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
+                        Settings.System.WEAR_TTS_PREWARM_ENABLED,
+                        Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
+                        Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
+                );
+        if (!Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
+            settings.add(Settings.System.MIN_REFRESH_RATE);
+            settings.add(Settings.System.PEAK_REFRESH_RATE);
+        }
+        return settings;
+    }
+
     private static void checkSettingsBackedUpOrDenied(
             Set<String> settings, Set<String> settingsToBackup, Set<String> denylist) {
         Set<String> settingsNotBackedUp = difference(settings, settingsToBackup);
diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig
new file mode 100644
index 0000000..1ad1666
--- /dev/null
+++ b/packages/SystemUI/aconfig/predictive_back.aconfig
@@ -0,0 +1,22 @@
+package: "com.android.systemui"
+
+flag {
+    name: "predictive_back_sysui"
+    namespace: "systemui"
+    description: "Predictive Back Dispatching for SysUI"
+    bug: "309545085"
+}
+
+flag {
+    name: "predictive_back_animate_shade"
+    namespace: "systemui"
+    description: "Enable Shade Animations"
+    bug: "309545085"
+}
+
+flag {
+    name: "predictive_back_animate_bouncer"
+    namespace: "systemui"
+    description: "Enable Predictive Back Animation in Bouncer"
+    bug: "309545085"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index f0093ab..5b21854 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -165,7 +165,7 @@
    name: "qs_new_tiles"
    namespace: "systemui"
    description: "Use the new tiles in the Quick Settings. Should have no behavior changes."
-   bug: "241772429"
+   bug: "311147395"
 }
 
 flag {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 1fc2843..c8461d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -45,10 +45,10 @@
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
 import com.android.systemui.classifier.FalsingA11yDelegate
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.log.SessionTracker
@@ -140,7 +140,7 @@
     @Mock private lateinit var viewMediatorCallback: ViewMediatorCallback
     @Mock private lateinit var audioManager: AudioManager
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     @Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var postureController: DevicePostureController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index e5da1f8..36aa441 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -85,10 +85,10 @@
 import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.FalsingManager;
@@ -336,7 +336,7 @@
                 mSessionTracker,
                 mAlternateBouncerInteractor,
                 mInputManager,
-                mock(KeyguardFaceAuthInteractor.class),
+                mock(DeviceEntryFaceAuthInteractor.class),
                 mUdfpsKeyguardAccessibilityDelegate,
                 mSelectedUserInteractor,
                 mFpsUnlockTracker,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 1f8854f..335ac9d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -32,11 +32,11 @@
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
@@ -111,7 +111,7 @@
                 FakeTrustRepository(),
                 testScope.backgroundScope,
                 mSelectedUserInteractor,
-                mock(KeyguardFaceAuthInteractor::class.java),
+                mock(DeviceEntryFaceAuthInteractor::class.java),
             )
         mAlternateBouncerInteractor =
             AlternateBouncerInteractor(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 99c1874..93ba6a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.res.R
 import com.android.systemui.scene.SceneTestUtils
 import com.google.common.truth.Truth.assertThat
@@ -46,7 +46,7 @@
 @RunWith(AndroidJUnit4::class)
 class BouncerInteractorTest : SysuiTestCase() {
 
-    @Mock private lateinit var keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var mDeviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor
 
     private val utils = SceneTestUtils(this)
     private val testScope = utils.testScope
@@ -67,7 +67,7 @@
         underTest =
             utils.bouncerInteractor(
                 authenticationInteractor = authenticationInteractor,
-                keyguardFaceAuthInteractor = keyguardFaceAuthInteractor,
+                deviceEntryFaceAuthInteractor = mDeviceEntryFaceAuthInteractor,
             )
     }
 
@@ -306,7 +306,7 @@
     fun intentionalUserInputEvent_notifiesFaceAuthInteractor() =
         testScope.runTest {
             underTest.onIntentionalUserInput()
-            verify(keyguardFaceAuthInteractor).onPrimaryBouncerUserInput()
+            verify(mDeviceEntryFaceAuthInteractor).onPrimaryBouncerUserInput()
         }
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
index bdf5041..c8560c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
@@ -26,9 +26,9 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.utils.os.FakeHandler
@@ -54,7 +54,7 @@
     @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     private val mainHandler = FakeHandler(Looper.getMainLooper())
     private lateinit var underTest: PrimaryBouncerInteractor
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index a3bf3f4..a0c2acc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -30,9 +30,9 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -63,7 +63,7 @@
     @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
 
     lateinit var bouncerInteractor: PrimaryBouncerInteractor
     private val mainHandler = FakeHandler(Looper.getMainLooper())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
similarity index 96%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 941d67f..6a14220 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.data.repository
+package com.android.systemui.deviceentry.data.repository
 
 import android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
 import android.app.StatusBarManager.SESSION_KEYGUARD
@@ -37,10 +37,6 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId.fakeInstanceId
 import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
@@ -53,21 +49,32 @@
 import com.android.systemui.coroutines.FlowValue
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.display.data.repository.FakeDisplayRepository
 import com.android.systemui.display.data.repository.display
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags.KEYGUARD_WM_STATE_REFACTOR
+import com.android.systemui.keyguard.data.repository.BiometricType
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.log.FaceAuthenticationLogger
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 2b41178..3a26ebf 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -66,6 +66,7 @@
         "kotlinx_coroutines",
         "dagger2",
         "jsr330",
+        "com_android_systemui_shared_flags_lib",
     ],
     resource_dirs: [
         "res",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 131eb6b..c08b083 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -20,10 +20,11 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
+import static com.android.systemui.shared.Flags.shadeAllowBackGesture;
+
 import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.SystemProperties;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicyConstants;
 
@@ -132,8 +133,7 @@
             SYSUI_STATE_WAKEFULNESS_TRANSITION | SYSUI_STATE_AWAKE;
 
     // Whether the back gesture is allowed (or ignored) by the Shade
-    public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean(
-            "persist.wm.debug.shade_allow_back_gesture", false);
+    public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = shadeAllowBackGesture();
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 5e35e77..b6b8c2a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -78,10 +78,10 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.classifier.FalsingA11yDelegate;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.ActivityStarter;
@@ -135,7 +135,7 @@
     private final SessionTracker mSessionTracker;
     private final Optional<SideFpsController> mSideFpsController;
     private final FalsingA11yDelegate mFalsingA11yDelegate;
-    private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     private final BouncerMessageInteractor mBouncerMessageInteractor;
     private int mTranslationY;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -216,7 +216,7 @@
         @Override
         public void onUserInput() {
             mBouncerMessageInteractor.onPrimaryBouncerUserInput();
-            mKeyguardFaceAuthInteractor.onPrimaryBouncerUserInput();
+            mDeviceEntryFaceAuthInteractor.onPrimaryBouncerUserInput();
         }
 
         @Override
@@ -347,11 +347,11 @@
     private final SwipeListener mSwipeListener = new SwipeListener() {
         @Override
         public void onSwipeUp() {
-            if (mKeyguardFaceAuthInteractor.canFaceAuthRun()) {
+            if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) {
                 mKeyguardSecurityCallback.userActivity();
             }
-            mKeyguardFaceAuthInteractor.onSwipeUpOnBouncer();
-            if (mKeyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) {
+            mDeviceEntryFaceAuthInteractor.onSwipeUpOnBouncer();
+            if (mDeviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) {
                 mUpdateMonitor.requestActiveUnlock(
                         ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
                         "swipeUpOnBouncer");
@@ -456,7 +456,7 @@
             TelephonyManager telephonyManager,
             ViewMediatorCallback viewMediatorCallback,
             AudioManager audioManager,
-            KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+            DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
             BouncerMessageInteractor bouncerMessageInteractor,
             Provider<JavaAdapter> javaAdapter,
             SelectedUserInteractor selectedUserInteractor,
@@ -495,7 +495,7 @@
         mTelephonyManager = telephonyManager;
         mViewMediatorCallback = viewMediatorCallback;
         mAudioManager = audioManager;
-        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+        mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
         mBouncerMessageInteractor = bouncerMessageInteractor;
         mSelectedUserInteractor = selectedUserInteractor;
         mDeviceEntryInteractor = deviceEntryInteractor;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e6b08c8..e59868e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -120,18 +120,19 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.FaceAuthenticationListener;
+import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus;
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.DumpsysTableLogger;
-import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
-import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus;
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.clocks.WeatherData;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -360,7 +361,7 @@
     @Nullable
     private final BiometricManager mBiometricManager;
     @Nullable
-    private KeyguardFaceAuthInteractor mFaceAuthInteractor;
+    private DeviceEntryFaceAuthInteractor mFaceAuthInteractor;
     private final DevicePostureController mDevicePostureController;
     private final TaskStackChangeListeners mTaskStackChangeListeners;
     private final IActivityTaskManager mActivityTaskManager;
@@ -1272,14 +1273,14 @@
         return getFaceAuthInteractor() != null && getFaceAuthInteractor().isRunning();
     }
 
-    private @Nullable KeyguardFaceAuthInteractor getFaceAuthInteractor() {
+    private @Nullable DeviceEntryFaceAuthInteractor getFaceAuthInteractor() {
         return mFaceAuthInteractor;
     }
 
     /**
      * Set the face auth interactor that should be used for initiating face authentication.
      */
-    public void setFaceAuthInteractor(KeyguardFaceAuthInteractor faceAuthInteractor) {
+    public void setFaceAuthInteractor(DeviceEntryFaceAuthInteractor faceAuthInteractor) {
         if (mFaceAuthInteractor != null) {
             mFaceAuthInteractor.unregisterListener(mFaceAuthenticationListener);
         }
@@ -1362,7 +1363,7 @@
      * @return whether the current user has been authenticated with face. This may be true
      * on the lockscreen if the user doesn't have bypass enabled.
      *
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()}
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isAuthenticated()}
      */
     @Deprecated
     public boolean getIsFaceAuthenticated() {
@@ -1393,7 +1394,7 @@
 
     /**
      * Returns whether the user is unlocked with face.
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()} instead
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isAuthenticated()} instead
      */
     @Deprecated
     public boolean isCurrentUserUnlockedWithFace() {
@@ -2464,7 +2465,7 @@
 
     /**
      * @return true if there's at least one face enrolled
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
      */
     @Deprecated
     public boolean isFaceEnabledAndEnrolled() {
@@ -2844,7 +2845,7 @@
     /**
      * If face auth is allows to scan on this exact moment.
      *
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#canFaceAuthRun()}
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#canFaceAuthRun()}
      */
     @Deprecated
     public boolean shouldListenForFace() {
@@ -2915,7 +2916,7 @@
     }
 
     /**
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#isLockedOut()}
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isLockedOut()}
      */
     @Deprecated
     public boolean isFaceLockedOut() {
@@ -2956,7 +2957,7 @@
     }
 
     /**
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
      */
     @VisibleForTesting
     @Deprecated
diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 066cba23..6076f32 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -22,10 +22,9 @@
 import android.window.OnBackInvokedDispatcher
 import android.window.WindowOnBackInvokedDispatcher
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.predictiveBackAnimateShade
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
 import com.android.systemui.shade.QuickSettingsController
@@ -48,14 +47,13 @@
     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
     private val shadeController: ShadeController,
     private val notificationShadeWindowController: NotificationShadeWindowController,
-    private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor,
-    featureFlags: FeatureFlags,
+    private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor
 ) : CoreStartable {
 
     private var isCallbackRegistered = false
 
     private val callback =
-        if (featureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE)) {
+        if (predictiveBackAnimateShade()) {
             /**
              * New callback that handles back gesture invoked, cancel, progress and provides
              * feedback via Shade animation.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
index cb75049..3ea1632 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
@@ -22,7 +22,7 @@
 import android.view.accessibility.AccessibilityNodeInfo
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.res.R
 import javax.inject.Inject
 
@@ -35,7 +35,7 @@
 @Inject
 constructor(
     @Main private val resources: Resources,
-    private val faceAuthInteractor: KeyguardFaceAuthInteractor,
+    private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
 ) : View.AccessibilityDelegate() {
     override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
         super.onInitializeAccessibilityNodeInfo(host, info)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 2fd13b3..d664637 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -80,11 +80,11 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.FalsingManager;
@@ -149,7 +149,7 @@
     @NonNull private final DumpManager mDumpManager;
     @NonNull private final SystemUIDialogManager mDialogManager;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    @NonNull private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     @NonNull private final VibratorHelper mVibrator;
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final PowerManager mPowerManager;
@@ -664,7 +664,7 @@
             @NonNull SessionTracker sessionTracker,
             @NonNull AlternateBouncerInteractor alternateBouncerInteractor,
             @NonNull InputManager inputManager,
-            @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+            @NonNull DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
             @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
             @NonNull SelectedUserInteractor selectedUserInteractor,
             @NonNull FpsUnlockTracker fpsUnlockTracker,
@@ -736,7 +736,7 @@
                     }
                     return Unit.INSTANCE;
                 });
-        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+        mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
 
         final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
         mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
@@ -1027,7 +1027,7 @@
         if (!mOnFingerDown) {
             playStartHaptic();
 
-            mKeyguardFaceAuthInteractor.onUdfpsSensorTouched();
+            mDeviceEntryFaceAuthInteractor.onUdfpsSensorTouched();
         }
         mOnFingerDown = true;
         mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 81d822f..c8ce245 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -29,7 +29,7 @@
 import com.android.systemui.classifier.domain.interactor.FalsingInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.res.R
 import javax.inject.Inject
@@ -52,7 +52,7 @@
     @Application private val applicationContext: Context,
     private val repository: BouncerRepository,
     private val authenticationInteractor: AuthenticationInteractor,
-    private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
+    private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
     private val falsingInteractor: FalsingInteractor,
     private val powerInteractor: PowerInteractor,
     private val simBouncerInteractor: SimBouncerInteractor,
@@ -100,7 +100,7 @@
      * user's pocket or by the user's face while holding their device up to their ear.
      */
     fun onIntentionalUserInput() {
-        keyguardFaceAuthInteractor.onPrimaryBouncerUserInput()
+        deviceEntryFaceAuthInteractor.onPrimaryBouncerUserInput()
         powerInteractor.onUserTouch()
         falsingInteractor.updateFalseConfidence(FalsingClassifier.Result.passed(0.6))
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index b587ed8..ef4554c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -30,9 +30,9 @@
 import com.android.systemui.bouncer.shared.model.Message
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.flags.SystemPropertiesHelper
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.TrustRepository
 import com.android.systemui.res.R.string.bouncer_face_not_recognized
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 621ca5d..654fa22 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -38,9 +38,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.shared.system.SysUiStatsLog
@@ -77,7 +77,7 @@
     private val trustRepository: TrustRepository,
     @Application private val applicationScope: CoroutineScope,
     private val selectedUserInteractor: SelectedUserInteractor,
-    private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
+    private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
 ) {
     private val passiveAuthBouncerDelay =
         context.resources.getInteger(R.integer.primary_bouncer_passive_auth_delay).toLong()
@@ -429,7 +429,7 @@
                 keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState()
 
         return !needsFullscreenBouncer() &&
-            (keyguardFaceAuthInteractor.canFaceAuthRun() || canRunActiveUnlock)
+            (deviceEntryFaceAuthInteractor.canFaceAuthRun() || canRunActiveUnlock)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
index b915418..71b5ab2 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.deviceentry
 
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import dagger.Module
 import dagger.multibindings.Multibinds
@@ -9,6 +10,7 @@
     includes =
         [
             DeviceEntryRepositoryModule::class,
+            FaceWakeUpTriggersConfigModule::class,
         ],
 )
 abstract class DeviceEntryModule {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index 17d7836..7a70c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.data.repository
+package com.android.systemui.deviceentry.data.repository
 
 import android.app.StatusBarManager
 import android.content.Context
@@ -22,7 +22,6 @@
 import android.os.CancellationSignal
 import com.android.internal.logging.InstanceId
 import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
 import com.android.systemui.Dumpable
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -32,19 +31,27 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.BiometricType
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FaceAuthTableLog
+import com.android.systemui.keyguard.data.repository.FaceDetectTableLog
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.data.repository.TrustRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.log.FaceAuthenticationLogger
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfig.kt
similarity index 83%
rename from packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfig.kt
index 84a6b09..9d6718b 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfig.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,28 +14,35 @@
  * limitations under the License.
  */
 
-package com.android.keyguard
+package com.android.systemui.deviceentry.data.repository
 
 import android.content.res.Resources
 import android.os.Build
 import android.os.PowerManager
 import com.android.systemui.Dumpable
-import com.android.systemui.res.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.res.R
 import com.android.systemui.util.settings.GlobalSettings
+import dagger.Binds
+import dagger.Module
 import java.io.PrintWriter
 import java.util.stream.Collectors
 import javax.inject.Inject
 
 /** Determines which device wake-ups should trigger passive authentication. */
+interface FaceWakeUpTriggersConfig {
+    fun shouldTriggerFaceAuthOnWakeUpFrom(@PowerManager.WakeReason pmWakeReason: Int): Boolean
+    fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean
+}
+
 @SysUISingleton
-class FaceWakeUpTriggersConfig
+class FaceWakeUpTriggersConfigImpl
 @Inject
 constructor(@Main resources: Resources, globalSettings: GlobalSettings, dumpManager: DumpManager) :
-    Dumpable {
+    Dumpable, FaceWakeUpTriggersConfig {
     private val defaultTriggerFaceAuthOnWakeUpFrom: Set<Int> =
         resources.getIntArray(R.array.config_face_auth_wake_up_triggers).toSet()
     private val triggerFaceAuthOnWakeUpFrom: Set<Int>
@@ -65,11 +72,13 @@
         dumpManager.registerDumpable(this)
     }
 
-    fun shouldTriggerFaceAuthOnWakeUpFrom(@PowerManager.WakeReason pmWakeReason: Int): Boolean {
+    override fun shouldTriggerFaceAuthOnWakeUpFrom(
+        @PowerManager.WakeReason pmWakeReason: Int
+    ): Boolean {
         return triggerFaceAuthOnWakeUpFrom.contains(pmWakeReason)
     }
 
-    fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean =
+    override fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean =
         wakeSleepReasonsToTriggerFaceAuth.contains(wakeReason)
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -87,3 +96,8 @@
             ?: default
     }
 }
+
+@Module
+interface FaceWakeUpTriggersConfigModule {
+    @Binds fun repository(impl: FaceWakeUpTriggersConfigImpl): FaceWakeUpTriggersConfig
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/NoopDeviceEntryFaceAuthRepository.kt
similarity index 68%
rename from packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/NoopDeviceEntryFaceAuthRepository.kt
index f4a74f0..6695b182 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -1,25 +1,25 @@
 /*
- *   Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
- *   Licensed under the Apache License, Version 2.0 (the "License");
- *   you may not use this file except in compliance with the License.
- *   You may obtain a copy of the License at
+ * 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
+ *      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.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
-package com.android.systemui.keyguard.data.repository
+package com.android.systemui.deviceentry.data.repository
 
-import com.android.keyguard.FaceAuthUiEvent
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
index 1a6bd04..5699176 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
@@ -18,13 +18,14 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.shared.DeviceEntryBiometricMode
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 
@@ -62,7 +63,9 @@
     val faceOnlyFaceFailure: Flow<FailedFaceAuthenticationStatus> =
         faceOnly.flatMapLatest { faceOnly ->
             if (faceOnly) {
-                deviceEntryFaceAuthInteractor.faceFailure
+                deviceEntryFaceAuthInteractor.authenticationStatus.filterIsInstance<
+                    FailedFaceAuthenticationStatus
+                >()
             } else {
                 emptyFlow()
             }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
index 70716c6..99bd25b 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,19 +16,80 @@
 
 package com.android.systemui.deviceentry.domain.interactor
 
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
-import javax.inject.Inject
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.filterIsInstance
 
-@SysUISingleton
-class DeviceEntryFaceAuthInteractor
-@Inject
-constructor(
-    repository: DeviceEntryFaceAuthRepository,
-) {
-    val faceFailure: Flow<FailedFaceAuthenticationStatus> =
-        repository.authenticationStatus.filterIsInstance<FailedFaceAuthenticationStatus>()
+/**
+ * Interactor that exposes API to get the face authentication status and handle any events that can
+ * cause face authentication to run for device entry.
+ */
+interface DeviceEntryFaceAuthInteractor {
+
+    /** Current authentication status */
+    val authenticationStatus: Flow<FaceAuthenticationStatus>
+
+    /** Current detection status */
+    val detectionStatus: Flow<FaceDetectionStatus>
+
+    /** Can face auth be run right now */
+    fun canFaceAuthRun(): Boolean
+
+    /** Whether face auth is currently running or not. */
+    fun isRunning(): Boolean
+
+    /** Whether face auth is in lock out state. */
+    fun isLockedOut(): Boolean
+
+    /** Whether face auth is enrolled and enabled for the current user */
+    fun isFaceAuthEnabledAndEnrolled(): Boolean
+
+    /** Whether the current user is authenticated successfully with face auth */
+    fun isAuthenticated(): Boolean
+    /**
+     * Register listener for use from code that cannot use [authenticationStatus] or
+     * [detectionStatus]
+     */
+    fun registerListener(listener: FaceAuthenticationListener)
+
+    /** Unregister previously registered listener */
+    fun unregisterListener(listener: FaceAuthenticationListener)
+
+    fun onUdfpsSensorTouched()
+    fun onAssistantTriggeredOnLockScreen()
+    fun onDeviceLifted()
+    fun onQsExpansionStared()
+    fun onNotificationPanelClicked()
+    fun onSwipeUpOnBouncer()
+    fun onPrimaryBouncerUserInput()
+    fun onAccessibilityAction()
+    fun onWalletLaunched()
+
+    /** Whether face auth is considered class 3 */
+    fun isFaceAuthStrong(): Boolean
+}
+
+/**
+ * Listener that can be registered with the [DeviceEntryFaceAuthInteractor] to receive updates about
+ * face authentication & detection updates.
+ *
+ * This is present to make it easier for use the new face auth API for code that cannot use
+ * [DeviceEntryFaceAuthInteractor.authenticationStatus] or
+ * [DeviceEntryFaceAuthInteractor.detectionStatus] flows.
+ */
+interface FaceAuthenticationListener {
+    /** Receive face isAuthenticated updates */
+    fun onAuthenticatedChanged(isAuthenticated: Boolean)
+
+    /** Receive face authentication status updates */
+    fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
+
+    /** Receive status updates whenever face detection runs */
+    fun onDetectionStatusChanged(status: FaceDetectionStatus)
+
+    fun onLockoutStateChanged(isLockedOut: Boolean)
+
+    fun onRunningStateChanged(isRunning: Boolean)
+
+    fun onAuthEnrollmentStateChanged(enrolled: Boolean)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index f6a9570..2680328 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -20,8 +20,8 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.TrustRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.scene.domain.interactor.SceneInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
new file mode 100644
index 0000000..3b94166
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+
+/**
+ * Implementation of the interactor that noops all face auth operations.
+ *
+ * This is required for SystemUI variants that do not support face authentication but still inject
+ * other SysUI components that depend on [DeviceEntryFaceAuthInteractor]
+ */
+@SysUISingleton
+class NoopDeviceEntryFaceAuthInteractor @Inject constructor() : DeviceEntryFaceAuthInteractor {
+    override val authenticationStatus: Flow<FaceAuthenticationStatus>
+        get() = emptyFlow()
+    override val detectionStatus: Flow<FaceDetectionStatus>
+        get() = emptyFlow()
+
+    override fun canFaceAuthRun(): Boolean = false
+
+    override fun isRunning(): Boolean = false
+
+    override fun isLockedOut(): Boolean = false
+
+    override fun isFaceAuthEnabledAndEnrolled(): Boolean = false
+
+    override fun isFaceAuthStrong(): Boolean = false
+
+    override fun isAuthenticated(): Boolean = false
+
+    override fun registerListener(listener: FaceAuthenticationListener) {}
+
+    override fun unregisterListener(listener: FaceAuthenticationListener) {}
+
+    override fun onUdfpsSensorTouched() {}
+
+    override fun onAssistantTriggeredOnLockScreen() {}
+
+    override fun onDeviceLifted() {}
+
+    override fun onQsExpansionStared() {}
+
+    override fun onNotificationPanelClicked() {}
+
+    override fun onSwipeUpOnBouncer() {}
+    override fun onPrimaryBouncerUserInput() {}
+    override fun onAccessibilityAction() {}
+    override fun onWalletLaunched() = Unit
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index e3f4739..98130eb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -14,14 +14,12 @@
  *   limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.deviceentry.domain.interactor
 
 import android.app.trust.TrustManager
 import android.content.Context
 import android.hardware.biometrics.BiometricFaceConstants
 import android.hardware.biometrics.BiometricSourceType
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceWakeUpTriggersConfig
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.CoreStartable
 import com.android.systemui.biometrics.data.repository.FacePropertyRepository
@@ -32,11 +30,14 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.log.FaceAuthenticationLogger
 import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -65,7 +66,7 @@
  * SystemUI Keyguard.
  */
 @SysUISingleton
-class SystemUIKeyguardFaceAuthInteractor
+class SystemUIDeviceEntryFaceAuthInteractor
 @Inject
 constructor(
     private val context: Context,
@@ -84,7 +85,7 @@
     private val powerInteractor: PowerInteractor,
     private val biometricSettingsRepository: BiometricSettingsRepository,
     private val trustManager: TrustManager,
-) : CoreStartable, KeyguardFaceAuthInteractor {
+) : CoreStartable, DeviceEntryFaceAuthInteractor {
 
     private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
 
@@ -310,7 +311,7 @@
     }
 
     companion object {
-        const val TAG = "KeyguardFaceAuthInteractor"
+        const val TAG = "DeviceEntryFaceAuthInteractor"
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
index 2abb7a4..ee220d5 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,58 +14,55 @@
  * limitations under the License.
  */
 
-package com.android.keyguard
+package com.android.systemui.deviceentry.shared
 
 import android.annotation.StringDef
 import android.os.PowerManager
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.ACCESSIBILITY_ACTION
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.NOTIFICATION_PANEL_CLICKED
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.PICK_UP_GESTURE_TRIGGERED
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.QS_EXPANDED
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.SWIPE_UP_ON_BOUNCER
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.UDFPS_POINTER_DOWN
-import com.android.keyguard.InternalFaceAuthReasons.ALL_AUTHENTICATORS_REGISTERED
-import com.android.keyguard.InternalFaceAuthReasons.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
-import com.android.keyguard.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION
-import com.android.keyguard.InternalFaceAuthReasons.BIOMETRIC_ENABLED
-import com.android.keyguard.InternalFaceAuthReasons.CAMERA_LAUNCHED
-import com.android.keyguard.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE
-import com.android.keyguard.InternalFaceAuthReasons.DISPLAY_OFF
-import com.android.keyguard.InternalFaceAuthReasons.DREAM_STARTED
-import com.android.keyguard.InternalFaceAuthReasons.DREAM_STOPPED
-import com.android.keyguard.InternalFaceAuthReasons.ENROLLMENTS_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.FACE_AUTHENTICATED
-import com.android.keyguard.InternalFaceAuthReasons.FACE_AUTH_STOPPED_ON_USER_INPUT
-import com.android.keyguard.InternalFaceAuthReasons.FACE_CANCEL_NOT_RECEIVED
-import com.android.keyguard.InternalFaceAuthReasons.FACE_LOCKOUT_RESET
-import com.android.keyguard.InternalFaceAuthReasons.FINISHED_GOING_TO_SLEEP
-import com.android.keyguard.InternalFaceAuthReasons.FP_AUTHENTICATED
-import com.android.keyguard.InternalFaceAuthReasons.FP_LOCKED_OUT
-import com.android.keyguard.InternalFaceAuthReasons.GOING_TO_SLEEP
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_GOING_AWAY
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_INIT
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_RESET
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
-import com.android.keyguard.InternalFaceAuthReasons.POSTURE_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
-import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
-import com.android.keyguard.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
-import com.android.keyguard.InternalFaceAuthReasons.STARTED_WAKING_UP
-import com.android.keyguard.InternalFaceAuthReasons.STRONG_AUTH_ALLOWED_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.TRUST_DISABLED
-import com.android.keyguard.InternalFaceAuthReasons.TRUST_ENABLED
-import com.android.keyguard.InternalFaceAuthReasons.USER_SWITCHING
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.ACCESSIBILITY_ACTION
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.NOTIFICATION_PANEL_CLICKED
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.PICK_UP_GESTURE_TRIGGERED
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.QS_EXPANDED
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.SWIPE_UP_ON_BOUNCER
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.UDFPS_POINTER_DOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ALL_AUTHENTICATORS_REGISTERED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.BIOMETRIC_ENABLED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.CAMERA_LAUNCHED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DISPLAY_OFF
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DREAM_STARTED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DREAM_STOPPED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ENROLLMENTS_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_AUTHENTICATED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_AUTH_STOPPED_ON_USER_INPUT
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_CANCEL_NOT_RECEIVED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_LOCKOUT_RESET
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FINISHED_GOING_TO_SLEEP
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FP_AUTHENTICATED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FP_LOCKED_OUT
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.GOING_TO_SLEEP
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_GOING_AWAY
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_INIT
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_RESET
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.POSTURE_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.STARTED_WAKING_UP
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.STRONG_AUTH_ALLOWED_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.TRUST_DISABLED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.TRUST_ENABLED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.USER_SWITCHING
 
-/**
- * List of reasons why face auth is requested by clients through
- * [KeyguardUpdateMonitor.requestFaceAuth].
- */
+/** List of reasons why face auth is requested by clients. */
 @Retention(AnnotationRetention.SOURCE)
 @StringDef(
     SWIPE_UP_ON_BOUNCER,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/FaceAuthenticationModels.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/FaceAuthenticationModels.kt
index 3de3666..f006b34 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/FaceAuthenticationModels.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.shared.model
+package com.android.systemui.deviceentry.shared.model
 
 import android.hardware.face.FaceManager
 import android.os.SystemClock.elapsedRealtime
 
 /**
  * Authentication status provided by
- * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
+ * [com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository]
  */
 sealed class FaceAuthenticationStatus
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index bb0c273..38c7c6a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -444,29 +444,10 @@
     // TODO(b/254512728): Tracking Bug
     @JvmField val NEW_BACK_AFFORDANCE = releasedFlag("new_back_affordance")
 
-    // TODO(b/255854141): Tracking Bug
-    @JvmField
-    val WM_ENABLE_PREDICTIVE_BACK_SYSUI =
-        unreleasedFlag("persist.wm.debug.predictive_back_sysui_enable", teamfood = true)
 
     // TODO(b/270987164): Tracking Bug
     @JvmField val TRACKPAD_GESTURE_FEATURES = releasedFlag("trackpad_gesture_features")
 
-    // TODO(b/263826204): Tracking Bug
-    @JvmField
-    val WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM =
-        unreleasedFlag("persist.wm.debug.predictive_back_bouncer_anim", teamfood = true)
-
-    // TODO(b/238475428): Tracking Bug
-    @JvmField
-    val WM_SHADE_ALLOW_BACK_GESTURE =
-        sysPropBooleanFlag("persist.wm.debug.shade_allow_back_gesture", default = false)
-
-    // TODO(b/238475428): Tracking Bug
-    @JvmField
-    val WM_SHADE_ANIMATE_BACK_GESTURE =
-        unreleasedFlag("persist.wm.debug.shade_animate_back_gesture", teamfood = false)
-
     // TODO(b/265639042): Tracking Bug
     @JvmField
     val WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
index d8eb81c..f29b657 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
@@ -16,10 +16,10 @@
 
 package com.android.systemui.keyguard.dagger
 
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.data.repository.NoopDeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
-import com.android.systemui.keyguard.domain.interactor.NoopKeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.NoopDeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.NoopDeviceEntryFaceAuthInteractor
 import dagger.Binds
 import dagger.Module
 
@@ -33,7 +33,9 @@
 @Module
 interface KeyguardFaceAuthNotSupportedModule {
     @Binds
-    fun keyguardFaceAuthInteractor(impl: NoopKeyguardFaceAuthInteractor): KeyguardFaceAuthInteractor
+    fun keyguardFaceAuthInteractor(
+        impl: NoopDeviceEntryFaceAuthInteractor
+    ): DeviceEntryFaceAuthInteractor
 
     @Binds
     fun deviceEntryFaceAuthRepository(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
index ee0eb2d..b373f85 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
@@ -19,8 +19,10 @@
 
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
-import com.android.systemui.keyguard.domain.interactor.SystemUIKeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepositoryImpl
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.SystemUIDeviceEntryFaceAuthInteractor
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
 import dagger.Binds
@@ -38,13 +40,13 @@
 
     @Binds
     @IntoMap
-    @ClassKey(SystemUIKeyguardFaceAuthInteractor::class)
-    fun bind(impl: SystemUIKeyguardFaceAuthInteractor): CoreStartable
+    @ClassKey(SystemUIDeviceEntryFaceAuthInteractor::class)
+    fun bind(impl: SystemUIDeviceEntryFaceAuthInteractor): CoreStartable
 
     @Binds
     fun keyguardFaceAuthInteractor(
-        impl: SystemUIKeyguardFaceAuthInteractor
-    ): KeyguardFaceAuthInteractor
+        impl: SystemUIDeviceEntryFaceAuthInteractor
+    ): DeviceEntryFaceAuthInteractor
 
     companion object {
         @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
deleted file mode 100644
index 046916a..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- *   Copyright (C) 2023 The Android Open Source Project
- *
- *   Licensed under the Apache License, Version 2.0 (the "License");
- *   you may not use this file except in compliance with the License.
- *   You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *   Unless required by applicable law or agreed to in writing, software
- *   distributed under the License is distributed on an "AS IS" BASIS,
- *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *   See the License for the specific language governing permissions and
- *   limitations under the License.
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import kotlinx.coroutines.flow.Flow
-
-/**
- * Interactor that exposes API to get the face authentication status and handle any events that can
- * cause face authentication to run.
- */
-interface KeyguardFaceAuthInteractor {
-
-    /** Current authentication status */
-    val authenticationStatus: Flow<FaceAuthenticationStatus>
-
-    /** Current detection status */
-    val detectionStatus: Flow<FaceDetectionStatus>
-
-    /** Can face auth be run right now */
-    fun canFaceAuthRun(): Boolean
-
-    /** Whether face auth is currently running or not. */
-    fun isRunning(): Boolean
-
-    /** Whether face auth is in lock out state. */
-    fun isLockedOut(): Boolean
-
-    /** Whether face auth is enrolled and enabled for the current user */
-    fun isFaceAuthEnabledAndEnrolled(): Boolean
-
-    /** Whether the current user is authenticated successfully with face auth */
-    fun isAuthenticated(): Boolean
-    /**
-     * Register listener for use from code that cannot use [authenticationStatus] or
-     * [detectionStatus]
-     */
-    fun registerListener(listener: FaceAuthenticationListener)
-
-    /** Unregister previously registered listener */
-    fun unregisterListener(listener: FaceAuthenticationListener)
-
-    fun onUdfpsSensorTouched()
-    fun onAssistantTriggeredOnLockScreen()
-    fun onDeviceLifted()
-    fun onQsExpansionStared()
-    fun onNotificationPanelClicked()
-    fun onSwipeUpOnBouncer()
-    fun onPrimaryBouncerUserInput()
-    fun onAccessibilityAction()
-    fun onWalletLaunched()
-
-    /** Whether face auth is considered class 3 */
-    fun isFaceAuthStrong(): Boolean
-}
-
-/**
- * Listener that can be registered with the [KeyguardFaceAuthInteractor] to receive updates about
- * face authentication & detection updates.
- *
- * This is present to make it easier for use the new face auth API for code that cannot use
- * [KeyguardFaceAuthInteractor.authenticationStatus] or [KeyguardFaceAuthInteractor.detectionStatus]
- * flows.
- */
-interface FaceAuthenticationListener {
-    /** Receive face isAuthenticated updates */
-    fun onAuthenticatedChanged(isAuthenticated: Boolean)
-
-    /** Receive face authentication status updates */
-    fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
-
-    /** Receive status updates whenever face detection runs */
-    fun onDetectionStatusChanged(status: FaceDetectionStatus)
-
-    fun onLockoutStateChanged(isLockedOut: Boolean)
-
-    fun onRunningStateChanged(isRunning: Boolean)
-
-    fun onAuthEnrollmentStateChanged(enrolled: Boolean)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
deleted file mode 100644
index cd6ab31..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *   Copyright (C) 2023 The Android Open Source Project
- *
- *   Licensed under the Apache License, Version 2.0 (the "License");
- *   you may not use this file except in compliance with the License.
- *   You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *   Unless required by applicable law or agreed to in writing, software
- *   distributed under the License is distributed on an "AS IS" BASIS,
- *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *   See the License for the specific language governing permissions and
- *   limitations under the License.
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.emptyFlow
-
-/**
- * Implementation of the interactor that noops all face auth operations.
- *
- * This is required for SystemUI variants that do not support face authentication but still inject
- * other SysUI components that depend on [KeyguardFaceAuthInteractor]
- */
-@SysUISingleton
-class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInteractor {
-    override val authenticationStatus: Flow<FaceAuthenticationStatus>
-        get() = emptyFlow()
-    override val detectionStatus: Flow<FaceDetectionStatus>
-        get() = emptyFlow()
-
-    override fun canFaceAuthRun(): Boolean = false
-
-    override fun isRunning(): Boolean = false
-
-    override fun isLockedOut(): Boolean = false
-
-    override fun isFaceAuthEnabledAndEnrolled(): Boolean = false
-
-    override fun isFaceAuthStrong(): Boolean = false
-
-    override fun isAuthenticated(): Boolean = false
-
-    override fun registerListener(listener: FaceAuthenticationListener) {}
-
-    override fun unregisterListener(listener: FaceAuthenticationListener) {}
-
-    override fun onUdfpsSensorTouched() {}
-
-    override fun onAssistantTriggeredOnLockScreen() {}
-
-    override fun onDeviceLifted() {}
-
-    override fun onQsExpansionStared() {}
-
-    override fun onNotificationPanelClicked() {}
-
-    override fun onSwipeUpOnBouncer() {}
-    override fun onPrimaryBouncerUserInput() {}
-    override fun onAccessibilityAction() {}
-    override fun onWalletLaunched() = Unit
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt
index 942cd60..b3d0f918 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt
@@ -32,7 +32,7 @@
 import android.os.PowerManager.WAKE_REASON_UNKNOWN
 import android.util.Log
 import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
 
 /**
  * Wrapper for [FaceAuthenticateOptions] to convert SystemUI values to their corresponding value in
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
index 5344696b..24d0602 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
@@ -30,11 +30,11 @@
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
 import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule
-import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
 import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeClockSection
 import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
 import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeMediaSection
 import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeSmartspaceSection
 import com.android.systemui.util.kotlin.getOrNull
 import java.util.Optional
 import javax.inject.Inject
@@ -62,7 +62,7 @@
     aodNotificationIconsSection: AodNotificationIconsSection,
     aodBurnInSection: AodBurnInSection,
     communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
-    smartspaceSection: SmartspaceSection,
+    smartspaceSection: SplitShadeSmartspaceSection,
     clockSection: SplitShadeClockSection,
     mediaSection: SplitShadeMediaSection,
 ) : KeyguardBlueprint {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeSmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeSmartspaceSection.kt
new file mode 100644
index 0000000..8728ada
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeSmartspaceSection.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
+import javax.inject.Inject
+
+/*
+ * We need this class for the splitShadeBlueprint so `addViews` and `removeViews` will be called
+ * when switching to and from splitShade.
+ */
+class SplitShadeSmartspaceSection
+@Inject
+constructor(
+    keyguardClockViewModel: KeyguardClockViewModel,
+    keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
+    context: Context,
+    smartspaceController: LockscreenSmartspaceController,
+    keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
+) :
+    SmartspaceSection(
+        keyguardClockViewModel,
+        keyguardSmartspaceViewModel,
+        context,
+        smartspaceController,
+        keyguardUnlockAnimationController,
+    )
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 3c2facb..9e6c552 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -2,9 +2,9 @@
 
 import android.hardware.face.FaceManager
 import android.hardware.face.FaceSensorPropertiesInternal
-import com.android.keyguard.FaceAuthUiEvent
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
 import com.android.systemui.log.core.LogLevel.DEBUG
 import com.android.systemui.log.dagger.FaceAuthLog
 import com.android.systemui.power.shared.model.WakeSleepReason
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 1e67771..24cb8ff 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -20,6 +20,7 @@
 
 import com.android.systemui.common.data.repository.PackageChangeRepository;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepositoryImpl;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.LogBufferFactory;
 import com.android.systemui.log.LogcatEchoTracker;
@@ -462,7 +463,7 @@
 
     /**
      * Provides a {@link LogBuffer} for use by
-     * {@link com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepositoryImpl}.
+     * {@link DeviceEntryFaceAuthRepositoryImpl}.
      */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 3e50dd3..ac0bd29 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -49,6 +49,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
@@ -231,32 +232,39 @@
         if (!collapsedView && mQsTileRevealController != null) {
             mQsTileRevealController.updateRevealedTiles(tiles);
         }
-        boolean shouldChange = false;
+        boolean shouldChangeAll = false;
+        // If the new tiles are a prefix of the old tiles, we delete the extra tiles (from the old).
+        // If not (even if they share a prefix) we remove all and add all the new ones.
         if (tiles.size() <= mRecords.size()) {
             int i = 0;
+            // Iterate through the requested tiles and check if they are the same as the existing
+            // tiles.
             for (QSTile tile : tiles) {
                 if (tile != mRecords.get(i).tile) {
-                    shouldChange = true;
+                    shouldChangeAll = true;
                     break;
                 }
                 i++;
             }
 
-            // If the first tiles are the same as the new ones, remove any extras.
-            if (!shouldChange) {
-                while (i < mRecords.size()) {
-                    QSPanelControllerBase.TileRecord record = mRecords.get(i);
+            // If the first tiles are the same as the new ones, we reuse them and remove any extra
+            // tiles.
+            if (!shouldChangeAll && i < mRecords.size()) {
+                List<TileRecord> extraRecords = mRecords.subList(i, mRecords.size());
+                for (QSPanelControllerBase.TileRecord record : extraRecords) {
                     mView.removeTile(record);
                     record.tile.removeCallback(record.callback);
-                    i++;
                 }
+                extraRecords.clear();
                 mCachedSpecs = getTilesSpecs();
             }
         } else {
-            shouldChange = true;
+            shouldChangeAll = true;
         }
 
-        if (shouldChange) {
+        // If we detected that the existing tiles are different than the requested tiles, clear them
+        // and add the new tiles.
+        if (shouldChangeAll) {
             for (QSPanelControllerBase.TileRecord record : mRecords) {
                 mView.removeTile(record);
                 record.tile.removeCallback(record.callback);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index fb6bc38..a5c1cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -26,6 +26,7 @@
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.Flags.keyguardBottomAreaRefactor;
 import static com.android.systemui.Flags.migrateClocksToBlueprint;
+import static com.android.systemui.Flags.predictiveBackAnimateShade;
 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
 import static com.android.systemui.classifier.Classifier.GENERIC;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -121,6 +122,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.DumpsysTableLogger;
@@ -131,7 +133,6 @@
 import com.android.systemui.keyguard.KeyguardViewConfigurator;
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
@@ -327,7 +328,7 @@
     private final PulseExpansionHandler mPulseExpansionHandler;
     private final KeyguardBypassController mKeyguardBypassController;
     private final KeyguardUpdateMonitor mUpdateMonitor;
-    private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     private final ConversationNotificationManager mConversationNotificationManager;
     private final AuthController mAuthController;
     private final MediaHierarchyManager mMediaHierarchyManager;
@@ -779,7 +780,7 @@
             ActiveNotificationsInteractor activeNotificationsInteractor,
             ShadeAnimationInteractor shadeAnimationInteractor,
             KeyguardViewConfigurator keyguardViewConfigurator,
-            KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+            DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
             SplitShadeStateController splitShadeStateController,
             PowerInteractor powerInteractor,
             KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm,
@@ -891,7 +892,7 @@
         mShadeHeaderController = shadeHeaderController;
         mLayoutInflater = layoutInflater;
         mFeatureFlags = featureFlags;
-        mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE);
+        mAnimateBack = predictiveBackAnimateShade();
         mTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled(Flags.TRACKPAD_GESTURE_FEATURES);
         mFalsingCollector = falsingCollector;
         mWakeUpCoordinator = coordinator;
@@ -936,7 +937,7 @@
         mScreenOffAnimationController = screenOffAnimationController;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mLastDownEvents = new NPVCDownEventState.Buffer(MAX_DOWN_EVENT_BUFFER_SIZE);
-        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+        mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
 
         int currentMode = navigationModeController.addListener(
                 mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode));
@@ -2978,9 +2979,9 @@
                     mShadeLog.v("onMiddleClicked on Keyguard, mDozingOnDown: false");
                     // Try triggering face auth, this "might" run. Check
                     // KeyguardUpdateMonitor#shouldListenForFace to see when face auth won't run.
-                    mKeyguardFaceAuthInteractor.onNotificationPanelClicked();
+                    mDeviceEntryFaceAuthInteractor.onNotificationPanelClicked();
 
-                    if (mKeyguardFaceAuthInteractor.canFaceAuthRun()) {
+                    if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) {
                         mUpdateMonitor.requestActiveUnlock(
                                 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
                                 "lockScreenEmptySpaceTap");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 8397caa..1dff99d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -66,9 +66,9 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
@@ -152,7 +152,7 @@
     private final RecordingController mRecordingController;
     private final LockscreenGestureLogger mLockscreenGestureLogger;
     private final ShadeLogger mShadeLog;
-    private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     private final CastController mCastController;
     private final SplitShadeStateController mSplitShadeStateController;
     private final InteractionJankMonitor mInteractionJankMonitor;
@@ -338,7 +338,7 @@
             InteractionJankMonitor interactionJankMonitor,
             ShadeLogger shadeLog,
             DumpManager dumpManager,
-            KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+            DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
             ShadeRepository shadeRepository,
             ShadeInteractor shadeInteractor,
             ActiveNotificationsInteractor activeNotificationsInteractor,
@@ -384,7 +384,7 @@
         mLockscreenGestureLogger = lockscreenGestureLogger;
         mMetricsLogger = metricsLogger;
         mShadeLog = shadeLog;
-        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+        mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
         mCastController = castController;
         mInteractionJankMonitor = interactionJankMonitor;
         mShadeRepository = shadeRepository;
@@ -972,7 +972,7 @@
         // When expanding QS, let's authenticate the user if possible,
         // this will speed up notification actions.
         if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) {
-            mKeyguardFaceAuthInteractor.onQsExpansionStared();
+            mDeviceEntryFaceAuthInteractor.onQsExpansionStared();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
index 4b89615..9fdd0bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
@@ -18,7 +18,7 @@
 
 import android.os.PowerManager
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 57d49b2..6e3aabf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,6 +31,7 @@
 import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.Flags.predictiveBackSysui;
 
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
@@ -836,7 +837,7 @@
         mLightRevealScrim = lightRevealScrim;
 
         // Based on teamfood flag, turn predictive back dispatch on at runtime.
-        if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
+        if (predictiveBackSysui()) {
             mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index 32b3ac2..9f08633 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -28,7 +28,7 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.Assert
@@ -43,13 +43,13 @@
  */
 @SysUISingleton
 class KeyguardLiftController @Inject constructor(
-    private val context: Context,
-    private val statusBarStateController: StatusBarStateController,
-    private val asyncSensorManager: AsyncSensorManager,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
-    private val dumpManager: DumpManager,
-    private val selectedUserInteractor: SelectedUserInteractor,
+        private val context: Context,
+        private val statusBarStateController: StatusBarStateController,
+        private val asyncSensorManager: AsyncSensorManager,
+        private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+        private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
+        private val dumpManager: DumpManager,
+        private val selectedUserInteractor: SelectedUserInteractor,
 ) : Dumpable, CoreStartable {
 
     private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)
@@ -75,7 +75,7 @@
             // Not listening anymore since trigger events unregister themselves
             isListening = false
             updateListeningState()
-            keyguardFaceAuthInteractor.onDeviceLifted()
+            deviceEntryFaceAuthInteractor.onDeviceLifted()
             keyguardUpdateMonitor.requestActiveUnlock(
                 ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE,
                 "KeyguardLiftController")
@@ -113,7 +113,7 @@
         val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
                 !statusBarStateController.isDozing
 
-        val isFaceEnabled = keyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
+        val isFaceEnabled = deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
         val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled
         if (shouldListen != isListening) {
             isListening = shouldListen
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4999123..88347ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -18,6 +18,7 @@
 
 import static android.view.WindowInsets.Type.navigationBars;
 
+import static com.android.systemui.Flags.predictiveBackAnimateBouncer;
 import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
 import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -400,8 +401,7 @@
         mFoldAodAnimationController = sysUIUnfoldComponent
                 .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
         mAlternateBouncerInteractor = alternateBouncerInteractor;
-        mIsBackAnimationEnabled =
-                featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM);
+        mIsBackAnimationEnabled = predictiveBackAnimateBouncer();
         mUdfpsOverlayInteractor = udfpsOverlayInteractor;
         mActivityStarter = activityStarter;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 5558aa7..111492c 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -43,7 +43,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.res.R;
@@ -69,7 +69,7 @@
     private final Executor mExecutor;
     private final Handler mHandler;
     private final FalsingManager mFalsingManager;
-    private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     private FalsingCollector mFalsingCollector;
     private final UserTracker mUserTracker;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -94,7 +94,7 @@
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             StatusBarKeyguardViewManager keyguardViewManager,
             UiEventLogger uiEventLogger,
-            KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
+            DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor) {
         mKeyguardStateController = keyguardStateController;
         mKeyguardDismissUtil = keyguardDismissUtil;
         mActivityStarter = activityStarter;
@@ -106,7 +106,7 @@
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mKeyguardViewManager = keyguardViewManager;
         mUiEventLogger = uiEventLogger;
-        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+        mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
     }
 
     @Override
@@ -213,7 +213,7 @@
                 true,
                 Utils.getColorAttrDefaultColor(
                         this, com.android.internal.R.attr.colorAccentPrimary));
-        mKeyguardFaceAuthInteractor.onWalletLaunched();
+        mDeviceEntryFaceAuthInteractor.onWalletLaunched();
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 9642895..bb98595 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -125,12 +125,14 @@
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig;
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigImpl;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.FaceAuthenticationListener;
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus;
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus;
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserTracker;
@@ -262,7 +264,7 @@
     @Mock
     private SelectedUserInteractor mSelectedUserInteractor;
     @Mock
-    private KeyguardFaceAuthInteractor mFaceAuthInteractor;
+    private DeviceEntryFaceAuthInteractor mFaceAuthInteractor;
     @Captor
     private ArgumentCaptor<FaceAuthenticationListener> mFaceAuthenticationListener;
 
@@ -316,7 +318,7 @@
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.systemui.res.R.integer.config_face_auth_supported_posture,
                 DEVICE_POSTURE_UNKNOWN);
-        mFaceWakeUpTriggersConfig = new FaceWakeUpTriggersConfig(
+        mFaceWakeUpTriggersConfig = new FaceWakeUpTriggersConfigImpl(
                 mContext.getResources(),
                 mGlobalSettings,
                 mDumpManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 8693d5c..7c626a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.back.domain.interactor
 
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import android.view.ViewRootImpl
 import android.window.BackEvent
 import android.window.BackEvent.EDGE_LEFT
@@ -26,9 +29,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -72,7 +74,6 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 class BackActionInteractorTest : SysuiTestCase() {
     private val testScope = TestScope()
-    private val featureFlags = FakeFeatureFlags()
     private val executor = FakeExecutor(FakeSystemClock())
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@@ -107,17 +108,17 @@
                 statusBarKeyguardViewManager,
                 shadeController,
                 notificationShadeWindowController,
-                windowRootViewVisibilityInteractor,
-                featureFlags,
+                windowRootViewVisibilityInteractor
             )
             .apply { this.setup(qsController, shadeViewController) }
     }
 
     private val powerInteractor = PowerInteractorFactory.create().powerInteractor
 
+    @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
     @Before
     fun setUp() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
         whenever(notificationShadeWindowController.windowRootView).thenReturn(windowRootView)
         whenever(windowRootView.viewRootImpl).thenReturn(viewRootImpl)
         whenever(viewRootImpl.onBackInvokedDispatcher).thenReturn(onBackInvokedDispatcher)
@@ -229,9 +230,9 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun animationFlagOff_onBackInvoked_keyguardNotified() {
         backActionInteractor.start()
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
         val callback = getBackInvokedCallback()
@@ -243,8 +244,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun animationFlagOn_onBackInvoked_keyguardNotified() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
@@ -257,8 +258,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun animationFlagOn_callbackIsAnimationCallback() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
@@ -269,8 +270,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun onBackProgressed_shadeCannotBeCollapsed_shadeViewControllerNotNotified() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
@@ -284,8 +285,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun onBackProgressed_shadeCanBeCollapsed_shadeViewControllerNotified() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
index 86b9b84..9b0e58d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
@@ -22,7 +22,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.whenever
@@ -42,7 +42,7 @@
 class FaceAuthAccessibilityDelegateTest : SysuiTestCase() {
 
     @Mock private lateinit var hostView: View
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     private lateinit var underTest: FaceAuthAccessibilityDelegate
 
     @Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index 755fa02..54dbd04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -57,13 +57,13 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.display.data.repository.FakeDisplayRepository
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
 import com.android.systemui.log.SideFpsLogger
 import com.android.systemui.log.logcatLogBuffer
@@ -110,7 +110,7 @@
     @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
     @Mock private lateinit var activityTaskManager: ActivityTaskManager
     @Mock private lateinit var displayManager: DisplayManager
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     @Mock
     private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
     @Mock private lateinit var fpsUnlockTracker: FpsUnlockTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index bdca948..1fa60fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -55,13 +55,13 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.display.data.repository.FakeDisplayRepository
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
 import com.android.systemui.log.SideFpsLogger
 import com.android.systemui.log.logcatLogBuffer
@@ -101,7 +101,7 @@
     @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
 
     @Mock private lateinit var activityTaskManager: ActivityTaskManager
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     @Mock
     private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index 45a426e..e796303 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -36,13 +36,13 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.flags.SystemPropertiesHelper
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.shared.model.AuthenticationFlags
 import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
 import com.android.systemui.res.R.string.kg_trust_agent_disabled
@@ -122,7 +122,7 @@
                 fakeTrustRepository,
                 testScope.backgroundScope,
                 mSelectedUserInteractor,
-                mock(KeyguardFaceAuthInteractor::class.java),
+                mock(DeviceEntryFaceAuthInteractor::class.java),
             )
         underTest =
             BouncerMessageInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index dacf23a..ee46f76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -32,9 +32,9 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.bouncer.ui.BouncerViewDelegate
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@@ -73,7 +73,7 @@
     @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     private lateinit var mainHandler: FakeHandler
     private lateinit var underTest: PrimaryBouncerInteractor
     private lateinit var resources: TestableResources
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
index 59bcf01..d5c3641 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
@@ -24,13 +24,13 @@
 import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
 import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.data.repository.powerRepository
 import com.android.systemui.power.shared.model.WakeSleepReason
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
similarity index 91%
rename from packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
index 94c34a5..e9b4bbb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.keyguard
+package com.android.systemui.deviceentry.data.repository
 
 import android.os.PowerManager
 import android.testing.AndroidTestingRunner
@@ -71,6 +71,6 @@
             wakeUpTriggers
         )
 
-        return FaceWakeUpTriggersConfig(mContext.getResources(), globalSettings, dumpManager)
+        return FaceWakeUpTriggersConfigImpl(mContext.resources, globalSettings, dumpManager)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
index 769cf45..368d1d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
@@ -1,21 +1,20 @@
 /*
- *  Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
+ * 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
+ *      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.
- *
+ * 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
+package com.android.systemui.deviceentry.domain.interactor
 
 import android.app.trust.TrustManager
 import android.content.pm.UserInfo
@@ -25,8 +24,6 @@
 import android.os.PowerManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceWakeUpTriggersConfig
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
@@ -42,6 +39,9 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
@@ -49,7 +49,8 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -87,9 +88,9 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
+class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() {
 
-    private lateinit var underTest: SystemUIKeyguardFaceAuthInteractor
+    private lateinit var underTest: SystemUIDeviceEntryFaceAuthInteractor
     private lateinit var testScope: TestScope
     private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
     private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
@@ -133,7 +134,7 @@
         fakeBiometricSettingsRepository = FakeBiometricSettingsRepository()
 
         underTest =
-            SystemUIKeyguardFaceAuthInteractor(
+            SystemUIDeviceEntryFaceAuthInteractor(
                 mContext,
                 testScope.backgroundScope,
                 dispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
similarity index 92%
rename from packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
index 68d0f41..ef89752 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.keyguard
+
+package com.android.systemui.deviceentry.shared
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index 027dfa1..c4df27c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
@@ -62,7 +63,7 @@
 class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
     @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
 
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index 1f245f1..7358b9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -147,7 +147,7 @@
                     trustRepository,
                     testScope.backgroundScope,
                     mSelectedUserInteractor,
-                    keyguardFaceAuthInteractor = mock(),
+                    deviceEntryFaceAuthInteractor = mock(),
                 ),
                 AlternateBouncerInteractor(
                     statusBarStateController = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 018fa9e..a92111e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -394,6 +394,7 @@
     public void setTiles_differentTiles_extraTileRemoved() {
         when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
         mController.setTiles();
+        assertEquals(2, mController.mRecords.size());
 
         clearInvocations(mQSPanel);
 
@@ -402,6 +403,7 @@
 
         verify(mQSPanel, times(1)).removeTile(any());
         verify(mQSPanel, never()).addTile(any());
+        assertEquals(1, mController.mRecords.size());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 2220756..3132767 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -86,6 +86,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
 import com.android.systemui.common.ui.view.LongPressHandlingView;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.dump.DumpManager;
@@ -98,7 +99,6 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -337,7 +337,7 @@
     protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener>
             mEmptySpaceClickListenerCaptor;
     @Mock protected ActivityStarter mActivityStarter;
-    @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    @Mock protected DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     @Mock private JavaAdapter mJavaAdapter;
     @Mock private CastController mCastController;
     @Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
@@ -382,7 +382,6 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false);
         mFeatureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false);
         mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
         mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
@@ -730,7 +729,7 @@
                 mActiveNotificationsInteractor,
                 mShadeAnimationInteractor,
                 mKeyguardViewConfigurator,
-                mKeyguardFaceAuthInteractor,
+                mDeviceEntryFaceAuthInteractor,
                 new ResourcesSplitShadeStateController(),
                 mPowerInteractor,
                 mKeyguardClockPositionAlgorithm,
@@ -800,7 +799,7 @@
                 mInteractionJankMonitor,
                 mShadeLog,
                 mDumpManager,
-                mKeyguardFaceAuthInteractor,
+                mDeviceEntryFaceAuthInteractor,
                 mShadeRepository,
                 mShadeInteractor,
                 mActiveNotificationsInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 3cbb9bb..2e8d46a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -1120,7 +1120,7 @@
         mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
         mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
 
-        verify(mKeyguardFaceAuthInteractor).onNotificationPanelClicked();
+        verify(mDeviceEntryFaceAuthInteractor).onNotificationPanelClicked();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index c21addc..ee7c6c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -62,7 +62,7 @@
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -257,7 +257,7 @@
                             FakeTrustRepository(),
                             testScope.backgroundScope,
                             mSelectedUserInteractor,
-                            mock(KeyguardFaceAuthInteractor::class.java)
+                            mock(DeviceEntryFaceAuthInteractor::class.java)
                         ),
                     facePropertyRepository = FakeFacePropertyRepository(),
                     deviceEntryFingerprintAuthRepository =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index eb5633b..727a6c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FakeFeatureFlagsClassic;
@@ -55,7 +56,6 @@
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
@@ -378,7 +378,7 @@
                 mInteractionJankMonitor,
                 mShadeLogger,
                 mDumpManager,
-                mock(KeyguardFaceAuthInteractor.class),
+                mock(DeviceEntryFaceAuthInteractor.class),
                 mShadeRepository,
                 mShadeInteractor,
                 mActiveNotificationsInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index e339636..316f2b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -334,15 +334,8 @@
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        // CentralSurfacesImpl's runtime flag check fails if the flag is absent.
-        // This value is unused, because test manifest is opted in.
-        mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
         // Set default value to avoid IllegalStateException.
         mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
-        // For the Shade to respond to Back gesture, we must enable the event routing
-        mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true);
-        // For the Shade to animate during the Back gesture, we must enable the animation flag.
-        mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true);
         mSetFlagsRule.enableFlags(FLAG_LIGHT_REVEAL_MIGRATION);
         // Turn AOD on and toggle feature flag for jank fixes
         mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 225ddb6..8dde935 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -37,6 +37,9 @@
 
 import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
 
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.service.trust.TrustAgentService;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -98,6 +101,7 @@
 import com.google.common.truth.Truth;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -165,6 +169,8 @@
     @Captor
     private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Before
     public void setUp() {
@@ -175,7 +181,6 @@
         when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
         when(mBouncerViewDelegate.getBackCallback()).thenReturn(mBouncerViewDelegateBackCallback);
         mFeatureFlags = new FakeFeatureFlags();
-        mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true);
         mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
         mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
         mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
@@ -584,6 +589,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_registration() {
         /* verify that a predictive back callback is registered when the bouncer becomes visible */
         mBouncerExpansionCallback.onVisibilityChanged(true);
@@ -598,6 +604,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_invocationHidesBouncer() {
         mBouncerExpansionCallback.onVisibilityChanged(true);
         /* capture the predictive back callback during registration */
@@ -615,6 +622,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_noBackAnimationForFullScreenBouncer() {
         when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
                 .thenReturn(KeyguardSecurityModel.SecurityMode.SimPin);
@@ -634,6 +642,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_forwardsBackDispatches() {
         mBouncerExpansionCallback.onVisibilityChanged(true);
         /* capture the predictive back callback during registration */
diff --git a/packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt
new file mode 100644
index 0000000..187935b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard
+
+import android.app.trust.TrustManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.trustManager by Kosmos.Fixture { mock<TrustManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt
new file mode 100644
index 0000000..6ef7419
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.facePropertyRepository by Fixture { FakeFacePropertyRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt
new file mode 100644
index 0000000..21cff0d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig by
+    Kosmos.Fixture { FakeFaceWakeUpTriggersConfig() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt
new file mode 100644
index 0000000..af617b7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.data.repository
+
+import com.android.systemui.power.shared.model.WakeSleepReason
+
+class FakeFaceWakeUpTriggersConfig : FaceWakeUpTriggersConfig {
+    private val triggerFaceAuthOnWakeUpFrom: MutableSet<Int> = mutableSetOf()
+    private val wakeSleepReasonsToTriggerFaceAuth: MutableSet<WakeSleepReason> = mutableSetOf()
+    override fun shouldTriggerFaceAuthOnWakeUpFrom(pmWakeReason: Int): Boolean {
+        return triggerFaceAuthOnWakeUpFrom.contains(pmWakeReason)
+    }
+
+    override fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean {
+        return wakeSleepReasonsToTriggerFaceAuth.contains(wakeReason)
+    }
+
+    fun setTriggerFaceAuthOnWakeUpFrom(pmWakeReasons: Set<Int>) {
+        triggerFaceAuthOnWakeUpFrom.clear()
+        triggerFaceAuthOnWakeUpFrom.addAll(pmWakeReasons)
+
+        wakeSleepReasonsToTriggerFaceAuth.clear()
+        wakeSleepReasonsToTriggerFaceAuth.addAll(
+            pmWakeReasons.map { WakeSleepReason.fromPowerManagerWakeReason(it) }
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
index d2dff78..0b1fb40 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
@@ -18,13 +18,45 @@
 
 package com.android.systemui.deviceentry.domain.interactor
 
+import android.content.applicationContext
+import com.android.keyguard.keyguardUpdateMonitor
+import com.android.keyguard.trustManager
+import com.android.systemui.biometrics.data.repository.facePropertyRepository
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.deviceentry.data.repository.faceWakeUpTriggersConfig
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.user.data.repository.userRepository
+import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
+val Kosmos.faceAuthLogger by Kosmos.Fixture { mock<FaceAuthenticationLogger>() }
 val Kosmos.deviceEntryFaceAuthInteractor by
     Kosmos.Fixture {
-        DeviceEntryFaceAuthInteractor(
+        SystemUIDeviceEntryFaceAuthInteractor(
+            context = applicationContext,
+            applicationScope = applicationCoroutineScope,
+            mainDispatcher = testDispatcher,
             repository = deviceEntryFaceAuthRepository,
+            primaryBouncerInteractor = { primaryBouncerInteractor },
+            alternateBouncerInteractor = alternateBouncerInteractor,
+            keyguardTransitionInteractor = keyguardTransitionInteractor,
+            faceAuthenticationLogger = faceAuthLogger,
+            keyguardUpdateMonitor = keyguardUpdateMonitor,
+            deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
+            userRepository = userRepository,
+            facePropertyRepository = facePropertyRepository,
+            faceWakeUpTriggersConfig = faceWakeUpTriggersConfig,
+            powerInteractor = powerInteractor,
+            biometricSettingsRepository = biometricSettingsRepository,
+            trustManager = trustManager,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
index 3d72967..599b5142 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.kosmos.Kosmos
 
 var Kosmos.deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index a1b6587..e96aeada 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -16,10 +16,11 @@
 
 package com.android.systemui.keyguard.data.repository
 
-import com.android.keyguard.FaceAuthUiEvent
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
 import dagger.Binds
 import dagger.Module
 import javax.inject.Inject
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index 3cabf0c..5f5d428 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -73,7 +74,7 @@
                 trustRepository,
                 testScope.backgroundScope,
                 mock(SelectedUserInteractor::class.java),
-                mock(KeyguardFaceAuthInteractor::class.java),
+                mock(DeviceEntryFaceAuthInteractor::class.java),
             )
         val alternateBouncerInteractor =
             AlternateBouncerInteractor(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 9f71161..09ab655 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -52,20 +52,20 @@
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
 import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.doze.DozeLogger
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.kosmos.Kosmos
@@ -266,14 +266,14 @@
 
     fun bouncerInteractor(
         authenticationInteractor: AuthenticationInteractor,
-        keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor = mock(),
+        deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor = mock(),
     ): BouncerInteractor {
         return BouncerInteractor(
             applicationScope = applicationScope(),
             applicationContext = context,
             repository = bouncerRepository,
             authenticationInteractor = authenticationInteractor,
-            keyguardFaceAuthInteractor = keyguardFaceAuthInteractor,
+            deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
             falsingInteractor = falsingInteractor(),
             powerInteractor = powerInteractor(),
             simBouncerInteractor = simBouncerInteractor,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7ac4dd3..a1b6f29 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7879,7 +7879,6 @@
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
-        DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_HDMI);
     }
 
     /** only public for mocking/spying, do not call outside of AudioService */
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index aa7f07d..35991b3 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -109,6 +109,11 @@
             Flags.FLAG_FAST_HDR_TRANSITIONS,
             Flags::fastHdrTransitions);
 
+    private final FlagState mRefreshRateVotingTelemetry = new FlagState(
+            Flags.FLAG_REFRESH_RATE_VOTING_TELEMETRY,
+            Flags::refreshRateVotingTelemetry
+    );
+
     /** Returns whether connected display management is enabled or not. */
     public boolean isConnectedDisplayManagementEnabled() {
         return mConnectedDisplayManagementFlagState.isEnabled();
@@ -220,6 +225,10 @@
         return mFastHdrTransitions.isEnabled();
     }
 
+    public boolean isRefreshRateVotingTelemetryEnabled() {
+        return mRefreshRateVotingTelemetry.isEnabled();
+    }
+
     /**
      * dumps all flagstates
      * @param pw printWriter
@@ -242,6 +251,7 @@
         pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState);
         pw.println(" " + mAutoBrightnessModesFlagState);
         pw.println(" " + mFastHdrTransitions);
+        pw.println(" " + mRefreshRateVotingTelemetry);
     }
 
     private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 04ecbb9..e735282 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -161,3 +161,10 @@
     is_fixed_read_only: true
 }
 
+flag {
+    name: "refresh_rate_voting_telemetry"
+    namespace: "display_manager"
+    description: "Feature flag for enabling telemetry for refresh rate voting in DisplayManager"
+    bug: "310029108"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 6e503cb..50e9533 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -154,6 +154,9 @@
 
     private final VotesStorage mVotesStorage;
 
+    @Nullable
+    private final VotesStatsReporter mVotesStatsReporter;
+
     /**
      * The allowed refresh rate switching type. This is used by SurfaceFlinger.
      */
@@ -204,6 +207,8 @@
         mContext = context;
         mHandler = new DisplayModeDirectorHandler(handler.getLooper());
         mInjector = injector;
+        mVotesStatsReporter = injector.getVotesStatsReporter(
+                displayManagerFlags.isRefreshRateVotingTelemetryEnabled());
         mSupportedModesByDisplay = new SparseArray<>();
         mDefaultModeByDisplay = new SparseArray<>();
         mAppRequestObserver = new AppRequestObserver();
@@ -214,7 +219,8 @@
         mBrightnessObserver = new BrightnessObserver(context, handler, injector);
         mDefaultDisplayDeviceConfig = null;
         mUdfpsObserver = new UdfpsObserver();
-        mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked);
+        mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked,
+                mVotesStatsReporter);
         mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage);
         mSensorObserver = new SensorObserver(context, mVotesStorage, injector);
         mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage);
@@ -341,6 +347,11 @@
             appRequestSummary.limitRefreshRanges(primarySummary);
 
             Display.Mode baseMode = primarySummary.selectBaseMode(availableModes, defaultMode);
+            if (mVotesStatsReporter != null) {
+                mVotesStatsReporter.reportVotesActivated(displayId, lowestConsideredPriority,
+                        baseMode, votes);
+            }
+
             if (baseMode == null) {
                 Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling"
                         + " back to the default mode. Display = " + displayId + ", votes = " + votes
@@ -970,9 +981,14 @@
 
                 if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
                     // The flag had been turned off, we need to restore the original value
-                    Settings.System.putFloatForUser(cr,
-                            Settings.System.MIN_REFRESH_RATE, minRefreshRate, cr.getUserId());
+                    Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE,
+                            highestRefreshRate, cr.getUserId());
                 }
+            } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
+                    && Math.round(minRefreshRate) == Math.round(highestRefreshRate)) {
+                // The flag has been turned on, we need to upgrade the setting
+                Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE,
+                        Float.POSITIVE_INFINITY, cr.getUserId());
             }
 
             float peakRefreshRate = Settings.System.getFloatForUser(cr,
@@ -983,9 +999,14 @@
 
                 if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
                     // The flag had been turned off, we need to restore the original value
-                    Settings.System.putFloatForUser(cr,
-                            Settings.System.PEAK_REFRESH_RATE, peakRefreshRate, cr.getUserId());
+                    Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE,
+                            highestRefreshRate, cr.getUserId());
                 }
+            } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
+                    && Math.round(peakRefreshRate) == Math.round(highestRefreshRate)) {
+                // The flag has been turned on, we need to upgrade the setting
+                Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE,
+                        Float.POSITIVE_INFINITY, cr.getUserId());
             }
 
             updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
@@ -2811,6 +2832,9 @@
         StatusBarManagerInternal getStatusBarManagerInternal();
 
         SensorManagerInternal getSensorManagerInternal();
+
+        @Nullable
+        VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled);
     }
 
     @VisibleForTesting
@@ -2943,6 +2967,13 @@
             return LocalServices.getService(SensorManagerInternal.class);
         }
 
+        @Override
+        public VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled) {
+            // if frame rate override supported, renderRates will be ignored in mode selection
+            return new VotesStatsReporter(supportsFrameRateOverride(),
+                    refreshRateVotingTelemetryEnabled);
+        }
+
         private DisplayManager getDisplayManager() {
             if (mDisplayManager == null) {
                 mDisplayManager = mContext.getSystemService(DisplayManager.class);
diff --git a/services/core/java/com/android/server/display/mode/VotesStatsReporter.java b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
new file mode 100644
index 0000000..a30c4d2
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Trace;
+import android.util.SparseArray;
+import android.view.Display;
+
+/**
+ * The VotesStatsReporter is responsible for collecting and sending Vote related statistics 
+ */
+class VotesStatsReporter {
+    private static final String TAG = "VotesStatsReporter";
+    private static final int REFRESH_RATE_NOT_LIMITED = 1000;
+    private final boolean mIgnoredRenderRate;
+    private final boolean mFrameworkStatsLogReportingEnabled;
+
+    public VotesStatsReporter(boolean ignoreRenderRate, boolean refreshRateVotingTelemetryEnabled) {
+        mIgnoredRenderRate = ignoreRenderRate;
+        mFrameworkStatsLogReportingEnabled = refreshRateVotingTelemetryEnabled;
+    }
+
+    void reportVoteAdded(int displayId, int priority,  @NonNull Vote vote) {
+        int maxRefreshRate = getMaxRefreshRate(vote, mIgnoredRenderRate);
+        Trace.traceCounter(Trace.TRACE_TAG_POWER,
+                TAG + "." + displayId + ":" + Vote.priorityToString(priority), maxRefreshRate);
+        // if ( mFrameworkStatsLogReportingEnabled) {
+        // FrameworkStatsLog.write(VOTE_CHANGED, displayID, priority, ADDED, maxRefreshRate, -1);
+        // }
+    }
+
+    void reportVoteRemoved(int displayId, int priority) {
+        Trace.traceCounter(Trace.TRACE_TAG_POWER,
+                TAG + "." + displayId + ":" + Vote.priorityToString(priority), -1);
+        // if ( mFrameworkStatsLogReportingEnabled) {
+        // FrameworkStatsLog.write(VOTE_CHANGED, displayID, priority, REMOVED, -1, -1);
+        // }
+    }
+
+    void reportVotesActivated(int displayId, int minPriority, @Nullable Display.Mode baseMode,
+            SparseArray<Vote> votes) {
+//        if (!mFrameworkStatsLogReportingEnabled) {
+//            return;
+//        }
+//        int selectedRefreshRate = baseMode != null ? (int) baseMode.getRefreshRate() : -1;
+//        for (int priority = minPriority; priority <= Vote.MAX_PRIORITY; priority ++) {
+//            Vote vote = votes.get(priority);
+//            if (vote != null) {
+//                int maxRefreshRate = getMaxRefreshRate(vote, mIgnoredRenderRate);
+//                FrameworkStatsLog.write(VOTE_CHANGED, displayId, priority,
+//                        ACTIVE, maxRefreshRate, selectedRefreshRate);
+//            }
+//        }
+    }
+
+    private static int getMaxRefreshRate(@NonNull Vote vote, boolean ignoreRenderRate) {
+        int maxRefreshRate = REFRESH_RATE_NOT_LIMITED;
+        if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) {
+            maxRefreshRate = (int) physicalVote.mMaxRefreshRate;
+        } else if (!ignoreRenderRate && (vote instanceof RefreshRateVote.RenderVote renderVote)) {
+            maxRefreshRate = (int)  renderVote.mMaxRefreshRate;
+        } else if (vote instanceof SupportedModesVote supportedModesVote) {
+            // SupportedModesVote limits mode by specific refreshRates, so highest rr is allowed
+            maxRefreshRate = 0;
+            for (SupportedModesVote.SupportedMode mode : supportedModesVote.mSupportedModes) {
+                maxRefreshRate = Math.max(maxRefreshRate, (int) mode.mPeakRefreshRate);
+            }
+        } else if (vote instanceof CombinedVote combinedVote) {
+            for (Vote subVote: combinedVote.mVotes) {
+                // CombinedVote should not have CombinedVote in mVotes, so recursion depth will be 1
+                maxRefreshRate = Math.min(maxRefreshRate,
+                        getMaxRefreshRate(subVote, ignoreRenderRate));
+            }
+        }
+        return maxRefreshRate;
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java
index 95fb8fc..7a1f7e9 100644
--- a/services/core/java/com/android/server/display/mode/VotesStorage.java
+++ b/services/core/java/com/android/server/display/mode/VotesStorage.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Trace;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -38,6 +37,9 @@
 
     private final Listener mListener;
 
+    @Nullable
+    private final VotesStatsReporter mVotesStatsReporter;
+
     private final Object mStorageLock = new Object();
     // A map from the display ID to the collection of votes and their priority. The latter takes
     // the form of another map from the priority to the vote itself so that each priority is
@@ -45,8 +47,9 @@
     @GuardedBy("mStorageLock")
     private final SparseArray<SparseArray<Vote>> mVotesByDisplay = new SparseArray<>();
 
-    VotesStorage(@NonNull Listener listener) {
+    VotesStorage(@NonNull Listener listener, @Nullable VotesStatsReporter votesStatsReporter) {
         mListener = listener;
+        mVotesStatsReporter = votesStatsReporter;
     }
     /** sets logging enabled/disabled for this class */
     void setLoggingEnabled(boolean loggingEnabled) {
@@ -110,17 +113,26 @@
                 changed = true;
             }
         }
-        Trace.traceCounter(Trace.TRACE_TAG_POWER,
-                TAG + "." + displayId + ":" + Vote.priorityToString(priority),
-                getMaxPhysicalRefreshRate(vote));
         if (mLoggingEnabled) {
             Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes);
         }
         if (changed) {
+            reportVoteStats(displayId, priority, vote);
             mListener.onChanged();
         }
     }
 
+    private void reportVoteStats(int displayId, int priority, @Nullable Vote vote) {
+        if (mVotesStatsReporter == null) {
+            return;
+        }
+        if (vote == null) {
+            mVotesStatsReporter.reportVoteRemoved(displayId, priority);
+        } else {
+            mVotesStatsReporter.reportVoteAdded(displayId, priority, vote);
+        }
+    }
+
     /** dump class values, for debugging */
     void dump(@NonNull PrintWriter pw) {
         SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>();
@@ -157,21 +169,6 @@
         }
     }
 
-    private static int getMaxPhysicalRefreshRate(@Nullable Vote vote) {
-        if (vote == null) {
-            return -1;
-        } else if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) {
-            return (int) physicalVote.mMaxRefreshRate;
-        } else if (vote instanceof CombinedVote combinedVote) {
-            return combinedVote.mVotes.stream()
-                    .filter(v -> v instanceof RefreshRateVote.PhysicalVote)
-                    .map(pv -> (int) (((RefreshRateVote.PhysicalVote) pv).mMaxRefreshRate))
-                    .min(Integer::compare)
-                    .orElse(1000); // for visualisation
-        }
-        return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable
-    }
-
     interface Listener {
         void onChanged();
     }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 24bcb4e..d722242 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.inputmethod;
 
+import static android.content.Context.DEVICE_ID_DEFAULT;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
@@ -315,10 +316,14 @@
     // All known input methods.
     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
     private final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
+
     // Mapping from deviceId to the device-specific imeId for that device.
+    @GuardedBy("ImfLock.class")
     private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>();
 
-    private final InputMethodSubtypeSwitchingController mSwitchingController;
+    // TODO: Instantiate mSwitchingController for each user.
+    @NonNull
+    private InputMethodSubtypeSwitchingController mSwitchingController;
     final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController =
             new HardwareKeyboardShortcutController();
 
@@ -339,6 +344,9 @@
     @GuardedBy("ImfLock.class")
     private int mDisplayIdToShowIme = INVALID_DISPLAY;
 
+    @GuardedBy("ImfLock.class")
+    private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
+
     @Nullable private StatusBarManagerInternal mStatusBarManagerInternal;
     private boolean mShowOngoingImeSwitcherForPhones;
     @GuardedBy("ImfLock.class")
@@ -1699,7 +1707,8 @@
 
         AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
         mSwitchingController =
-                InputMethodSubtypeSwitchingController.createInstanceLocked(mSettings, context);
+                InputMethodSubtypeSwitchingController.createInstanceLocked(context, mMethodMap,
+                        userId);
         mHardwareKeyboardShortcutController.reset(mSettings);
         mMenuController = new InputMethodMenuController(this);
         mBindingController =
@@ -2464,11 +2473,7 @@
             @StartInputReason int startInputReason,
             int unverifiedTargetSdkVersion,
             @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
-        // If no method is currently selected, do nothing.
-        final String selectedMethodId = getSelectedMethodIdLocked();
-        if (selectedMethodId == null) {
-            return InputBindResult.NO_IME;
-        }
+        String selectedMethodId = getSelectedMethodIdLocked();
 
         if (!mSystemReady) {
             // If the system is not yet ready, we shouldn't be running third
@@ -2493,8 +2498,21 @@
             return InputBindResult.NOT_IME_TARGET_WINDOW;
         }
         final int csDisplayId = cs.mSelfReportedDisplayId;
+        final int oldDisplayIdToShowIme = mDisplayIdToShowIme;
         mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId);
 
+        // Potentially override the selected input method if the new display belongs to a virtual
+        // device with a custom IME.
+        if (oldDisplayIdToShowIme != mDisplayIdToShowIme) {
+            final String deviceMethodId = computeCurrentDeviceMethodIdLocked(selectedMethodId);
+            if (deviceMethodId == null) {
+                mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
+            } else if (!Objects.equals(deviceMethodId, selectedMethodId)) {
+                setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme);
+                selectedMethodId = deviceMethodId;
+            }
+        }
+
         if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) {
             hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
                     null /* resultReceiver */,
@@ -2502,6 +2520,11 @@
             return InputBindResult.NO_IME;
         }
 
+        // If no method is currently selected, do nothing.
+        if (selectedMethodId == null) {
+            return InputBindResult.NO_IME;
+        }
+
         if (mCurClient != cs) {
             prepareClientSwitchLocked(cs);
         }
@@ -2568,6 +2591,62 @@
         return mBindingController.bindCurrentMethod();
     }
 
+    /**
+     * Update the current deviceId and return the relevant imeId for this device.
+     *   1. If the device changes to virtual and its custom IME is not available, then disable IME.
+     *   2. If the device changes to virtual with valid custom IME, then return the custom IME. If
+     *      the old device was default, then store the current imeId so it can be restored.
+     *   3. If the device changes to default, restore the default device IME.
+     *   4. Otherwise keep the current imeId.
+     */
+    @GuardedBy("ImfLock.class")
+    private String computeCurrentDeviceMethodIdLocked(String currentMethodId) {
+        if (mVdmInternal == null) {
+            mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
+        }
+        if (mVdmInternal == null || !android.companion.virtual.flags.Flags.vdmCustomIme()) {
+            return currentMethodId;
+        }
+
+        final int oldDeviceId = mDeviceIdToShowIme;
+        mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme);
+        if (mDeviceIdToShowIme == oldDeviceId) {
+            return currentMethodId;
+        }
+        if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+            final String defaultDeviceMethodId = mSettings.getSelectedDefaultDeviceInputMethod();
+            if (DEBUG) {
+                Slog.v(TAG, "Restoring default device input method: " + defaultDeviceMethodId);
+            }
+            return defaultDeviceMethodId;
+        }
+
+        final String deviceMethodId =
+                mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId);
+        if (Objects.equals(deviceMethodId, currentMethodId)) {
+            return currentMethodId;
+        } else if (!mMethodMap.containsKey(deviceMethodId)) {
+            if (DEBUG) {
+                Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme
+                        + " because its custom input method is not available: " + deviceMethodId);
+            }
+            return null;
+        }
+
+        if (oldDeviceId == DEVICE_ID_DEFAULT) {
+            if (DEBUG) {
+                Slog.v(TAG, "Storing default device input method " + currentMethodId);
+            }
+            mSettings.putSelectedDefaultDeviceInputMethod(currentMethodId);
+        }
+        if (DEBUG) {
+            Slog.v(TAG, "Switching current input method from " + currentMethodId
+                    + " to device-specific one " + deviceMethodId + " because the current display "
+                    + mDisplayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme);
+        }
+        return deviceMethodId;
+    }
+
     @GuardedBy("ImfLock.class")
     void invalidateAutofillSessionLocked() {
         mAutofillController.invalidateAutofillSession();
@@ -3218,13 +3297,16 @@
             // There is no longer an input method set, so stop any current one.
             resetCurrentMethodAndClientLocked(UnbindReason.NO_IME);
         }
-        // Here is not the perfect place to reset the switching controller. Ideally
-        // mSwitchingController and mSettings should be able to share the same state.
-        // TODO: Make sure that mSwitchingController and mSettings are sharing the
-        // the same enabled IMEs list.
-        mSwitchingController.resetCircularListLocked(mContext);
-        mHardwareKeyboardShortcutController.reset(mSettings);
 
+        // TODO: Instantiate mSwitchingController for each user.
+        if (mSettings.getCurrentUserId() == mSwitchingController.getUserId()) {
+            mSwitchingController.resetCircularListLocked(mMethodMap);
+        } else {
+            mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
+                    mContext, mMethodMap, mSettings.getCurrentUserId());
+        }
+
+        mHardwareKeyboardShortcutController.reset(mSettings);
         sendOnNavButtonFlagsChangedLocked();
     }
 
@@ -3242,6 +3324,11 @@
 
     @GuardedBy("ImfLock.class")
     void setInputMethodLocked(String id, int subtypeId) {
+        setInputMethodLocked(id, subtypeId, DEVICE_ID_DEFAULT);
+    }
+
+    @GuardedBy("ImfLock.class")
+    void setInputMethodLocked(String id, int subtypeId, int deviceId) {
         InputMethodInfo info = mMethodMap.get(id);
         if (info == null) {
             throw getExceptionForUnknownImeId(id);
@@ -3285,6 +3372,14 @@
         }
 
         // Changing to a different IME.
+        if (mDeviceIdToShowIme != DEVICE_ID_DEFAULT && deviceId == DEVICE_ID_DEFAULT) {
+            // This change should only be applicable to the default device but the current input
+            // method is a custom one specific to a virtual device. So only update the settings
+            // entry used to restore the default device input method once we want to show the IME
+            // back on the default device.
+            mSettings.putSelectedDefaultDeviceInputMethod(id);
+            return;
+        }
         IInputMethodInvoker curMethod = getCurMethodLocked();
         if (curMethod != null) {
             curMethod.removeStylusHandwritingWindow();
@@ -4598,6 +4693,9 @@
                 }
                 return;
             }
+            if (mSettings.getCurrentUserId() != mSwitchingController.getUserId()) {
+                return;
+            }
             final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
             if (imi != null) {
                 mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
@@ -4835,9 +4933,10 @@
                     int lastInputMethodSubtypeId =
                             mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
 
-                    final List<ImeSubtypeListItem> imList = mSwitchingController
-                            .getSortedInputMethodAndSubtypeListForImeMenuLocked(
-                                    showAuxSubtypes, isScreenLocked);
+                    final List<ImeSubtypeListItem> imList = InputMethodSubtypeSwitchingController
+                            .getSortedInputMethodAndSubtypeList(
+                                    showAuxSubtypes, isScreenLocked, false, mContext,
+                                    mMethodMap, mSettings.getCurrentUserId());
                     mMenuController.showInputMethodMenuLocked(showAuxSubtypes, displayId,
                             lastInputMethodId, lastInputMethodSubtypeId, imList);
                 }
@@ -5222,11 +5321,13 @@
 
         updateDefaultVoiceImeIfNeededLocked();
 
-        // Here is not the perfect place to reset the switching controller. Ideally
-        // mSwitchingController and mSettings should be able to share the same state.
-        // TODO: Make sure that mSwitchingController and mSettings are sharing the
-        // the same enabled IMEs list.
-        mSwitchingController.resetCircularListLocked(mContext);
+        // TODO: Instantiate mSwitchingController for each user.
+        if (mSettings.getCurrentUserId() == mSwitchingController.getUserId()) {
+            mSwitchingController.resetCircularListLocked(mMethodMap);
+        } else {
+            mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
+                    mContext, mMethodMap, mSettings.getCurrentUserId());
+        }
         mHardwareKeyboardShortcutController.reset(mSettings);
 
         sendOnNavButtonFlagsChangedLocked();
@@ -5308,11 +5409,21 @@
             StringBuilder builder = new StringBuilder();
             if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
                     builder, enabledInputMethodsList, id)) {
-                // Disabled input method is currently selected, switch to another one.
-                final String selId = mSettings.getSelectedInputMethod();
-                if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
-                    Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
-                    resetSelectedInputMethodAndSubtypeLocked("");
+                if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+                    // Disabled input method is currently selected, switch to another one.
+                    final String selId = mSettings.getSelectedInputMethod();
+                    if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
+                        Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
+                        resetSelectedInputMethodAndSubtypeLocked("");
+                    }
+                } else if (id.equals(mSettings.getSelectedDefaultDeviceInputMethod())) {
+                    // Disabled default device IME while using a virtual device one, choose a
+                    // new default one but only update the settings.
+                    InputMethodInfo newDefaultIme =
+                            InputMethodInfoUtils.getMostApplicableDefaultIME(
+                                        mSettings.getEnabledInputMethodListLocked());
+                    mSettings.putSelectedDefaultDeviceInputMethod(
+                            newDefaultIme == null ? "" : newDefaultIme.getId());
                 }
                 // Previous state was enabled.
                 return true;
@@ -5652,9 +5763,8 @@
 
         @Override
         public void setVirtualDeviceInputMethodForAllUsers(int deviceId, @Nullable String imeId) {
-            // TODO(b/287269288): validate that id belongs to a valid virtual device instead.
-            Preconditions.checkArgument(deviceId != Context.DEVICE_ID_DEFAULT,
-                    "DeviceId " + deviceId + " does not belong to a virtual device.");
+            Preconditions.checkArgument(deviceId != DEVICE_ID_DEFAULT,
+                    TextUtils.formatSimple("DeviceId %d is not a virtual device id.", deviceId));
             synchronized (ImfLock.class) {
                 if (imeId == null) {
                     mVirtualDeviceMethodMap.remove(deviceId);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 431aabd..4439b06 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -16,10 +16,14 @@
 
 package com.android.server.inputmethod;
 
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.Context;
-import android.content.pm.PackageManager;
+import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Printer;
 import android.util.Slog;
@@ -32,7 +36,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.Locale;
 import java.util.Objects;
 
 /**
@@ -154,79 +157,71 @@
         }
     }
 
-    private static class InputMethodAndSubtypeList {
-        private final Context mContext;
-        // Used to load label
-        private final PackageManager mPm;
-        private final String mSystemLocaleStr;
-        private final InputMethodSettings mSettings;
+    static List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
+            boolean includeAuxiliarySubtypes, boolean isScreenLocked, boolean forImeMenu,
+            @NonNull Context context, @NonNull ArrayMap<String, InputMethodInfo> methodMap,
+            @UserIdInt int userId) {
+        final Context userAwareContext = context.getUserId() == userId
+                ? context
+                : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
+        final String mSystemLocaleStr = SystemLocaleWrapper.get(userId).get(0).toLanguageTag();
+        final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
 
-        InputMethodAndSubtypeList(Context context, InputMethodSettings settings) {
-            mContext = context;
-            mSettings = settings;
-            mPm = context.getPackageManager();
-            final Locale locale = context.getResources().getConfiguration().locale;
-            mSystemLocaleStr = locale != null ? locale.toString() : "";
+        final ArrayList<InputMethodInfo> imis = settings.getEnabledInputMethodListLocked();
+        if (imis.isEmpty()) {
+            return Collections.emptyList();
         }
-
-        public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
-                boolean includeAuxiliarySubtypes, boolean isScreenLocked, boolean forImeMenu) {
-            final ArrayList<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
-            if (imis.isEmpty()) {
-                return Collections.emptyList();
+        if (isScreenLocked && includeAuxiliarySubtypes) {
+            if (DEBUG) {
+                Slog.w(TAG, "Auxiliary subtypes are not allowed to be shown in lock screen.");
             }
-            if (isScreenLocked && includeAuxiliarySubtypes) {
+            includeAuxiliarySubtypes = false;
+        }
+        final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
+        final int numImes = imis.size();
+        for (int i = 0; i < numImes; ++i) {
+            final InputMethodInfo imi = imis.get(i);
+            if (forImeMenu && !imi.shouldShowInInputMethodPicker()) {
+                continue;
+            }
+            final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList =
+                    settings.getEnabledInputMethodSubtypeListLocked(imi, true);
+            final ArraySet<String> enabledSubtypeSet = new ArraySet<>();
+            for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
+                enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
+            }
+            final CharSequence imeLabel = imi.loadLabel(userAwareContext.getPackageManager());
+            if (enabledSubtypeSet.size() > 0) {
+                final int subtypeCount = imi.getSubtypeCount();
                 if (DEBUG) {
-                    Slog.w(TAG, "Auxiliary subtypes are not allowed to be shown in lock screen.");
+                    Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
                 }
-                includeAuxiliarySubtypes = false;
-            }
-            final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
-            final int numImes = imis.size();
-            for (int i = 0; i < numImes; ++i) {
-                final InputMethodInfo imi = imis.get(i);
-                if (forImeMenu && !imi.shouldShowInInputMethodPicker()) {
-                    continue;
-                }
-                final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList =
-                        mSettings.getEnabledInputMethodSubtypeListLocked(imi, true);
-                final ArraySet<String> enabledSubtypeSet = new ArraySet<>();
-                for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
-                    enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
-                }
-                final CharSequence imeLabel = imi.loadLabel(mPm);
-                if (enabledSubtypeSet.size() > 0) {
-                    final int subtypeCount = imi.getSubtypeCount();
-                    if (DEBUG) {
-                        Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
-                    }
-                    for (int j = 0; j < subtypeCount; ++j) {
-                        final InputMethodSubtype subtype = imi.getSubtypeAt(j);
-                        final String subtypeHashCode = String.valueOf(subtype.hashCode());
-                        // We show all enabled IMEs and subtypes when an IME is shown.
-                        if (enabledSubtypeSet.contains(subtypeHashCode)
-                                && (includeAuxiliarySubtypes || !subtype.isAuxiliary())) {
-                            final CharSequence subtypeLabel =
-                                    subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
-                                            .getDisplayName(mContext, imi.getPackageName(),
-                                                    imi.getServiceInfo().applicationInfo);
-                            imList.add(new ImeSubtypeListItem(imeLabel,
-                                    subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
+                for (int j = 0; j < subtypeCount; ++j) {
+                    final InputMethodSubtype subtype = imi.getSubtypeAt(j);
+                    final String subtypeHashCode = String.valueOf(subtype.hashCode());
+                    // We show all enabled IMEs and subtypes when an IME is shown.
+                    if (enabledSubtypeSet.contains(subtypeHashCode)
+                            && (includeAuxiliarySubtypes || !subtype.isAuxiliary())) {
+                        final CharSequence subtypeLabel =
+                                subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
+                                        .getDisplayName(userAwareContext, imi.getPackageName(),
+                                                imi.getServiceInfo().applicationInfo);
+                        imList.add(new ImeSubtypeListItem(imeLabel,
+                                subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
 
-                            // Removing this subtype from enabledSubtypeSet because we no
-                            // longer need to add an entry of this subtype to imList to avoid
-                            // duplicated entries.
-                            enabledSubtypeSet.remove(subtypeHashCode);
-                        }
+                        // Removing this subtype from enabledSubtypeSet because we no
+                        // longer need to add an entry of this subtype to imList to avoid
+                        // duplicated entries.
+                        enabledSubtypeSet.remove(subtypeHashCode);
                     }
-                } else {
-                    imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null,
-                            mSystemLocaleStr));
                 }
+            } else {
+                imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null,
+                        mSystemLocaleStr));
             }
-            Collections.sort(imList);
-            return imList;
         }
+        Collections.sort(imList);
+        return imList;
     }
 
     private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) {
@@ -479,18 +474,32 @@
         }
     }
 
-    private final InputMethodSettings mSettings;
-    private InputMethodAndSubtypeList mSubtypeList;
+    private final Context mContext;
+    @UserIdInt
+    private final int mUserId;
     private ControllerImpl mController;
 
-    private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) {
-        mSettings = settings;
-        resetCircularListLocked(context);
+    private InputMethodSubtypeSwitchingController(@NonNull Context context,
+            @NonNull ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
+        mContext = context;
+        mUserId = userId;
+        mController = ControllerImpl.createFrom(null,
+                getSortedInputMethodAndSubtypeList(
+                        false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
+                        false /* forImeMenu */, context, methodMap, userId));
     }
 
+    @NonNull
     public static InputMethodSubtypeSwitchingController createInstanceLocked(
-            InputMethodSettings settings, Context context) {
-        return new InputMethodSubtypeSwitchingController(settings, context);
+            @NonNull Context context,
+            @NonNull ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
+        return new InputMethodSubtypeSwitchingController(context, methodMap, userId);
+    }
+
+    @AnyThread
+    @UserIdInt
+    int getUserId() {
+        return mUserId;
     }
 
     public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
@@ -503,12 +512,12 @@
         mController.onUserActionLocked(imi, subtype);
     }
 
-    public void resetCircularListLocked(Context context) {
-        mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
+    public void resetCircularListLocked(
+            @NonNull ArrayMap<String, InputMethodInfo> methodMap) {
         mController = ControllerImpl.createFrom(mController,
-                mSubtypeList.getSortedInputMethodAndSubtypeList(
+                getSortedInputMethodAndSubtypeList(
                         false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
-                        false /* forImeMenu */));
+                        false /* forImeMenu */, mContext, methodMap, mUserId));
     }
 
     public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
@@ -522,12 +531,6 @@
         return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
     }
 
-    public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListForImeMenuLocked(
-            boolean includingAuxiliarySubtypes, boolean isScreenLocked) {
-        return mSubtypeList.getSortedInputMethodAndSubtypeList(
-                includingAuxiliarySubtypes, isScreenLocked, true /* forImeMenu */);
-    }
-
     public void dump(final Printer pw) {
         if (mController != null) {
             mController.dump(pw);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index a0b55ed..f9b06de 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -51,6 +51,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.StringJoiner;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -241,6 +242,12 @@
                 Slog.d(TAG, "--- Switch the current user from " + mCurrentUserId + " to " + userId);
             }
             mCurrentUserId = userId;
+            String ime = getSelectedInputMethod();
+            String defaultDeviceIme = getSelectedDefaultDeviceInputMethod();
+            if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
+                putSelectedInputMethod(defaultDeviceIme);
+                putSelectedDefaultDeviceInputMethod(null);
+            }
         }
 
         private void putString(@NonNull String key, @Nullable String str) {
@@ -636,6 +643,24 @@
             return imi;
         }
 
+        @Nullable
+        String getSelectedDefaultDeviceInputMethod() {
+            final String imi = getString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null);
+            if (DEBUG) {
+                Slog.d(TAG, "getSelectedDefaultDeviceInputMethodStr: " + imi + ", "
+                        + mCurrentUserId);
+            }
+            return imi;
+        }
+
+        void putSelectedDefaultDeviceInputMethod(String imeId) {
+            if (DEBUG) {
+                Slog.d(TAG, "putSelectedDefaultDeviceInputMethodStr: " + imeId + ", "
+                        + mCurrentUserId);
+            }
+            putString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, imeId);
+        }
+
         void putDefaultVoiceInputMethod(String imeId) {
             if (DEBUG) {
                 Slog.d(TAG, "putDefaultVoiceInputMethodStr: " + imeId + ", " + mCurrentUserId);
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 41d0176..22951d5 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -180,7 +180,9 @@
         // priority of system overlays.
         final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
         for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
-            for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+            final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+                    apexInfo.apexModuleName);
+            for (String packageName : mApexManager.getApksInApex(apexPackageName)) {
                 apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index dcfc855d..376b061 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -25,6 +25,7 @@
 import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap;
 import static android.content.pm.ArchivedActivityInfo.drawableToBitmap;
 import static android.content.pm.PackageInstaller.EXTRA_UNARCHIVE_STATUS;
+import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
 import static android.content.pm.PackageManager.DELETE_ARCHIVE;
 import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
@@ -72,10 +73,12 @@
 import android.os.IBinder;
 import android.os.ParcelableException;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SELinux;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ExceptionUtils;
+import android.util.Pair;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -93,7 +96,9 @@
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 
@@ -140,15 +145,23 @@
     private final Context mContext;
     private final PackageManagerService mPm;
 
+    private final AppStateHelper mAppStateHelper;
+
     @Nullable
     private LauncherApps mLauncherApps;
 
     @Nullable
     private AppOpsManager mAppOpsManager;
 
+    /* IntentSender store that maps key: {userId, appPackageName} to respective existing attached
+     unarchival intent sender. */
+    private final Map<Pair<Integer, String>, IntentSender> mLauncherIntentSenders;
+
     PackageArchiver(Context context, PackageManagerService mPm) {
         this.mContext = context;
         this.mPm = mPm;
+        this.mAppStateHelper = new AppStateHelper(mContext);
+        this.mLauncherIntentSenders = new HashMap<>();
     }
 
     /** Returns whether a package is archived for a user. */
@@ -235,37 +248,32 @@
             // Return early as the calling UID does not match caller package's UID.
             return START_CLASS_NOT_FOUND;
         }
+
         String currentLauncherPackageName = getCurrentLauncherPackageName(userId);
         if ((currentLauncherPackageName == null || !callerPackageName.equals(
                 currentLauncherPackageName)) && callingUid != Process.SHELL_UID) {
             // TODO(b/311619990): Remove dependency on SHELL_UID for testing
             Slog.e(TAG, TextUtils.formatSimple(
-                    "callerPackageName: %s does not qualify for archival of package: " + "%s!",
+                    "callerPackageName: %s does not qualify for unarchival of package: " + "%s!",
                     callerPackageName, packageName));
             return START_PERMISSION_DENIED;
         }
-        // TODO(b/302114464): Handle edge cases & also divert to a dialog based on
-        //  permissions + compat options
-        Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName));
-        try {
-            final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
-                @Override
-                public void send(int code, Intent intent, String resolvedType,
-                        IBinder allowlistToken,
-                        IIntentReceiver finishedReceiver, String requiredPermission,
-                        Bundle options) {
-                    // TODO(b/302114464): Handle intent sender status codes
-                }
-            };
 
+        Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName));
+
+        try {
+            // TODO(b/311709794) Make showUnarchivalConfirmation dependent on the compat options.
             requestUnarchive(packageName, callerPackageName,
-                    new IntentSender((IIntentSender) mLocalSender), UserHandle.of(userId));
+                    getOrCreateUnarchiveIntentSender(userId, packageName),
+                    UserHandle.of(userId),
+                    false /* showUnarchivalConfirmation= */);
         } catch (Throwable t) {
             Slog.e(TAG, TextUtils.formatSimple(
                     "Unexpected error occurred while unarchiving package %s: %s.", packageName,
                     t.getLocalizedMessage()));
             return START_ABORTED;
         }
+
         return START_SUCCESS;
     }
 
@@ -321,6 +329,20 @@
         return true;
     }
 
+    private IntentSender getOrCreateUnarchiveIntentSender(int userId, String packageName) {
+        Pair<Integer, String> key = Pair.create(userId, packageName);
+        synchronized (mLauncherIntentSenders) {
+            IntentSender intentSender = mLauncherIntentSenders.get(key);
+            if (intentSender != null) {
+                return intentSender;
+            }
+            IntentSender unarchiveIntentSender = new IntentSender(
+                    (IIntentSender) new UnarchiveIntentSender());
+            mLauncherIntentSenders.put(key, unarchiveIntentSender);
+            return unarchiveIntentSender;
+        }
+    }
+
     /** Creates archived state for the package and user. */
     private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId)
             throws PackageManager.NameNotFoundException {
@@ -553,6 +575,15 @@
             @NonNull String callerPackageName,
             @NonNull IntentSender statusReceiver,
             @NonNull UserHandle userHandle) {
+        requestUnarchive(packageName, callerPackageName, statusReceiver, userHandle,
+                false /* showUnarchivalConfirmation= */);
+    }
+
+    private void requestUnarchive(
+            @NonNull String packageName,
+            @NonNull String callerPackageName,
+            @NonNull IntentSender statusReceiver,
+            @NonNull UserHandle userHandle, boolean showUnarchivalConfirmation) {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(callerPackageName);
         Objects.requireNonNull(statusReceiver);
@@ -597,8 +628,8 @@
                     + "an unarchival.");
         }
 
-        if (!hasInstallPackages) {
-            requestUnarchiveConfirmation(packageName, statusReceiver);
+        if (!hasInstallPackages || showUnarchivalConfirmation) {
+            requestUnarchiveConfirmation(packageName, statusReceiver, userHandle);
             return;
         }
 
@@ -622,7 +653,8 @@
                 () -> unarchiveInternal(packageName, userHandle, installerPackage, draftSessionId));
     }
 
-    private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver) {
+    private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver,
+            UserHandle user) {
         final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_DIALOG);
         dialogIntent.putExtra(EXTRA_UNARCHIVE_INTENT_SENDER, statusReceiver);
         dialogIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName);
@@ -632,6 +664,7 @@
         broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS,
                 PackageInstaller.STATUS_PENDING_USER_ACTION);
         broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent);
+        broadcastIntent.putExtra(Intent.EXTRA_USER, user);
         sendIntent(statusReceiver, packageName, /* message= */ "", broadcastIntent);
     }
 
@@ -656,6 +689,7 @@
 
         int installerUid = mPm.snapshotComputer().getPackageUid(installerPackage, 0, userId);
         // Handles case of repeated unarchival calls for the same package.
+        // TODO(b/316881759) Allow attaching multiple intentSenders to one session.
         int existingSessionId = mPm.mInstallerService.getExistingDraftSessionId(installerUid,
                 sessionParams,
                 userId);
@@ -849,7 +883,13 @@
 
     void notifyUnarchivalListener(int status, String installerPackageName, String appPackageName,
             long requiredStorageBytes, @Nullable PendingIntent userActionIntent,
-            IntentSender unarchiveIntentSender, int userId) {
+            @Nullable IntentSender unarchiveIntentSender, int userId) {
+        if (unarchiveIntentSender == null) {
+            // Maybe this can happen if the installer calls reportUnarchivalStatus twice in quick
+            // succession.
+            return;
+        }
+
         final Intent broadcastIntent = new Intent();
         broadcastIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, appPackageName);
         broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status);
@@ -863,6 +903,7 @@
                 return;
             }
             broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent);
+            broadcastIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
         }
 
         final BroadcastOptions options = BroadcastOptions.makeBasic();
@@ -874,6 +915,10 @@
                     options.toBundle());
         } catch (IntentSender.SendIntentException e) {
             Slog.e(TAG, TextUtils.formatSimple("Failed to send unarchive intent"), e);
+        } finally {
+            synchronized (mLauncherIntentSenders) {
+                mLauncherIntentSenders.remove(Pair.create(userId, appPackageName));
+            }
         }
     }
 
@@ -883,6 +928,7 @@
             long requiredStorageBytes, PendingIntent userActionIntent, int userId) {
         final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_ERROR_DIALOG);
         dialogIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status);
+        dialogIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
         if (requiredStorageBytes > 0) {
             dialogIntent.putExtra(EXTRA_REQUIRED_BYTES, requiredStorageBytes);
         }
@@ -1118,4 +1164,25 @@
 
         return activities.toArray(new ArchivedActivityParcel[activities.size()]);
     }
+
+    private class UnarchiveIntentSender extends IIntentSender.Stub {
+        @Override
+        public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+                IIntentReceiver finishedReceiver, String requiredPermission, Bundle options)
+                throws RemoteException {
+            int status = intent.getExtras().getInt(PackageInstaller.EXTRA_UNARCHIVE_STATUS,
+                    STATUS_PENDING_USER_ACTION);
+            if (status == UNARCHIVAL_OK) {
+                return;
+            }
+            Intent extraIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+            UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
+            if (extraIntent != null && user != null
+                    && mAppStateHelper.isAppTopVisible(
+                            getCurrentLauncherPackageName(user.getIdentifier()))) {
+                extraIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                mContext.startActivityAsUser(extraIntent, user);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 02e28dd..7bd6a43 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -487,6 +487,8 @@
         synchronized (mPm.mInstallLock) {
             cleanUpResourcesLI(codeFile, instructionSets);
         }
+        // TODO: open logging to help debug, will delete or add debug flag
+        Slog.d(TAG, "cleanUpResources for " + codeFile);
         if (packageName == null) {
             return;
         }
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 3ecc985..fdff89a 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -958,7 +958,8 @@
             final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                     Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
             if (vibrate) {
-                mVibrator.vibrate(CHARGING_VIBRATION_EFFECT,
+                mVibrator.vibrate(Process.SYSTEM_UID, mContext.getOpPackageName(),
+                        CHARGING_VIBRATION_EFFECT, /* reason= */ "Charging started",
                         HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
             }
 
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 1577cef..2d584c4 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -98,6 +98,8 @@
 import android.view.animation.Interpolator;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.TraceBuffer;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -297,6 +299,18 @@
         }
     }
 
+    /** It is only used by unit test. */
+    @VisibleForTesting
+    Surface forceShowMagnifierSurface(int displayId) {
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.mMagnifedViewport.mWindow.setAlpha(DisplayMagnifier.MagnifiedViewport
+                    .ViewportWindow.AnimationController.MAX_ALPHA);
+            return displayMagnifier.mMagnifedViewport.mWindow.mSurface;
+        }
+        return null;
+    }
+
     void onWindowLayersChanged(int displayId) {
         if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                 | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
@@ -448,6 +462,7 @@
         }
     }
 
+    // TODO(b/318327737): Remove parameter 't' when removing flag DRAW_IN_WM_LOCK.
     void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
         if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
             mAccessibilityTracing.logTrace(
@@ -1106,11 +1121,19 @@
             }
 
             void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
-                if (shown) {
+                if (ViewportWindow.DRAW_IN_WM_LOCK) {
+                    if (shown) {
+                        mFullRedrawNeeded = true;
+                        mOldMagnificationRegion.set(0, 0, 0, 0);
+                    }
+                    mWindow.setShown(shown, animate);
+                    return;
+                }
+                if (mWindow.setShown(shown, animate)) {
                     mFullRedrawNeeded = true;
+                    // Clear the old region, so recomputeBounds will refresh the current region.
                     mOldMagnificationRegion.set(0, 0, 0, 0);
                 }
-                mWindow.setShown(shown, animate);
             }
 
             void getMagnifiedFrameInContentCoords(Rect rect) {
@@ -1130,7 +1153,11 @@
 
             void drawWindowIfNeeded(SurfaceControl.Transaction t) {
                 recomputeBounds();
-                mWindow.drawIfNeeded(t);
+                if (ViewportWindow.DRAW_IN_WM_LOCK) {
+                    mWindow.drawOrRemoveIfNeeded(t);
+                    return;
+                }
+                mWindow.postDrawIfNeeded();
             }
 
             void destroyWindow() {
@@ -1158,23 +1185,28 @@
                 mWindow.dump(pw, prefix);
             }
 
-            private final class ViewportWindow {
+            private final class ViewportWindow implements Runnable {
                 private static final String SURFACE_TITLE = "Magnification Overlay";
+                // TODO(b/318327737): Remove if it is stable.
+                static final boolean DRAW_IN_WM_LOCK = !Flags.drawMagnifierBorderOutsideWmlock();
 
                 private final Region mBounds = new Region();
                 private final Rect mDirtyRect = new Rect();
                 private final Paint mPaint = new Paint();
 
                 private final SurfaceControl mSurfaceControl;
+                /** After initialization, it should only be accessed from animation thread. */
+                private final SurfaceControl.Transaction mTransaction;
                 private final BLASTBufferQueue mBlastBufferQueue;
                 private final Surface mSurface;
 
                 private final AnimationController mAnimationController;
 
                 private boolean mShown;
+                private boolean mLastSurfaceShown;
                 private int mAlpha;
 
-                private boolean mInvalidated;
+                private volatile boolean mInvalidated;
 
                 ViewportWindow(Context context) {
                     SurfaceControl surfaceControl = null;
@@ -1202,6 +1234,7 @@
                     InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t,
                             mDisplayContent.getDisplayId(), "Magnification Overlay");
                     t.apply();
+                    mTransaction = t;
                     mSurface = mBlastBufferQueue.createSurface();
 
                     mAnimationController = new AnimationController(context,
@@ -1219,10 +1252,11 @@
                     mInvalidated = true;
                 }
 
-                void setShown(boolean shown, boolean animate) {
+                /** Returns {@code true} if the state is changed to shown. */
+                boolean setShown(boolean shown, boolean animate) {
                     synchronized (mService.mGlobalLock) {
                         if (mShown == shown) {
-                            return;
+                            return false;
                         }
                         mShown = shown;
                         mAnimationController.onFrameShownStateChanged(shown, animate);
@@ -1230,6 +1264,7 @@
                             Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
                         }
                     }
+                    return shown;
                 }
 
                 @SuppressWarnings("unused")
@@ -1285,7 +1320,22 @@
                     mService.scheduleAnimationLocked();
                 }
 
-                void drawIfNeeded(SurfaceControl.Transaction t) {
+                void postDrawIfNeeded() {
+                    if (mInvalidated) {
+                        mService.mAnimationHandler.post(this);
+                    }
+                }
+
+                @Override
+                public void run() {
+                    drawOrRemoveIfNeeded(mTransaction);
+                }
+
+                /**
+                 * This method must only be called by animation handler directly to make sure
+                 * thread safe and there is no lock held outside.
+                 */
+                private void drawOrRemoveIfNeeded(SurfaceControl.Transaction t) {
                     // Drawing variables (alpha, dirty rect, and bounds) access is synchronized
                     // using WindowManagerGlobalLock. Grab copies of these values before
                     // drawing on the canvas so that drawing can be performed outside of the lock.
@@ -1293,6 +1343,14 @@
                     Rect drawingRect = null;
                     Region drawingBounds = null;
                     synchronized (mService.mGlobalLock) {
+                        if (!DRAW_IN_WM_LOCK && mBlastBufferQueue.mNativeObject == 0) {
+                            // Complete removal since releaseSurface has been called.
+                            if (mSurface.isValid()) {
+                                mTransaction.remove(mSurfaceControl).apply();
+                                mSurface.release();
+                            }
+                            return;
+                        }
                         if (!mInvalidated) {
                             return;
                         }
@@ -1314,6 +1372,7 @@
                         }
                     }
 
+                    final boolean showSurface;
                     // Draw without holding WindowManagerGlobalLock.
                     if (alpha > 0) {
                         Canvas canvas = null;
@@ -1329,18 +1388,38 @@
                         mPaint.setAlpha(alpha);
                         canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint);
                         mSurface.unlockCanvasAndPost(canvas);
-                        t.show(mSurfaceControl);
+                        if (DRAW_IN_WM_LOCK) {
+                            t.show(mSurfaceControl);
+                            return;
+                        }
+                        showSurface = true;
                     } else {
-                        t.hide(mSurfaceControl);
+                        if (DRAW_IN_WM_LOCK) {
+                            t.hide(mSurfaceControl);
+                            return;
+                        }
+                        showSurface = false;
+                    }
+
+                    if (showSurface && !mLastSurfaceShown) {
+                        mTransaction.show(mSurfaceControl).apply();
+                        mLastSurfaceShown = true;
+                    } else if (!showSurface && mLastSurfaceShown) {
+                        mTransaction.hide(mSurfaceControl).apply();
+                        mLastSurfaceShown = false;
                     }
                 }
 
+                @GuardedBy("mService.mGlobalLock")
                 void releaseSurface() {
-                    if (mBlastBufferQueue != null) {
-                        mBlastBufferQueue.destroy();
+                    mBlastBufferQueue.destroy();
+                    if (DRAW_IN_WM_LOCK) {
+                        mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
+                        mSurface.release();
+                        return;
                     }
-                    mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
-                    mSurface.release();
+                    // Post to perform cleanup on the thread which handles mSurface.
+                    mService.mAnimationHandler.post(this);
                 }
 
                 void dump(PrintWriter pw, String prefix) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 60a0c03..a0e5fd8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -3434,6 +3434,11 @@
             return mSensorManagerInternal;
         }
 
+        @Override
+        public VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled) {
+            return null;
+        }
+
         protected Display createDisplay(int id) {
             return new Display(DisplayManagerGlobal.getInstance(), id, mDisplayInfo,
                     ApplicationProvider.getApplicationContext().getResources());
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
index f677401..2815fcb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
@@ -58,7 +58,7 @@
     private RegisteringFakesInjector mInjector = new RegisteringFakesInjector();
 
     private final TestHandler mHandler = new TestHandler(null);
-    private final VotesStorage mStorage = new VotesStorage(() -> {});
+    private final VotesStorage mStorage = new VotesStorage(() -> {}, null);
 
     @Before
     public void setUp() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
index 50e2392..1f6f1a4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
@@ -51,7 +51,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mVotesStorage = new VotesStorage(mVotesListener);
+        mVotesStorage = new VotesStorage(mVotesListener, null);
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 113511e..321d945 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -73,6 +73,7 @@
         // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
         "testng",
         "compatibility-device-util-axt",
+        "flag-junit",
     ],
 
     libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 57326b2..d08cdc7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -343,10 +343,12 @@
         List<ApexManager.ScanResult> scanResults = scanApexInfos(apexInfo);
         mApexManager.notifyScanResult(scanResults);
 
-        assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName)).isNull();
+        final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+                activeApex.apexModuleName);
+        assertThat(mApexManager.getApkInApexInstallError(apexPackageName)).isNull();
         mApexManager.reportErrorWithApkInApex(activeApex.apexDirectory.getAbsolutePath(),
                 "Some random error");
-        assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName))
+        assertThat(mApexManager.getApkInApexInstallError(apexPackageName))
                 .isEqualTo("Some random error");
     }
 
@@ -370,9 +372,11 @@
         List<ApexManager.ScanResult> scanResults = scanApexInfos(apexInfo);
         mApexManager.notifyScanResult(scanResults);
 
-        assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty();
+        final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+                activeApex.apexModuleName);
+        assertThat(mApexManager.getApksInApex(apexPackageName)).isEmpty();
         mApexManager.registerApkInApex(fakeApkInApex);
-        assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty();
+        assertThat(mApexManager.getApksInApex(apexPackageName)).isEmpty();
     }
 
     @Test
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 91d8ceb..a9ff3a1 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -113,7 +114,8 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device vibrates once
-        verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class));
+        verify(mVibrator, times(1)).vibrate(anyInt(), any(), any(), any(),
+                any(VibrationAttributes.class));
     }
 
     @Test
@@ -129,7 +131,7 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device doesn't vibrate
-        verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
+        verifyZeroInteractions(mVibrator);
     }
 
     @Test
@@ -145,14 +147,15 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device vibrates once
-        verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class));
+        verify(mVibrator, times(1)).vibrate(anyInt(), any(), any(), any(),
+                any(VibrationAttributes.class));
     }
 
     @Test
     public void testVibrateDisabled_wirelessCharging() {
         createNotifier();
 
-        // GIVEN the charging vibration is disabeld
+        // GIVEN the charging vibration is disabled
         enableChargingVibration(false);
 
         // WHEN wireless charging starts
@@ -161,7 +164,7 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device doesn't vibrate
-        verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
+        verifyZeroInteractions(mVibrator);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 8bf4833..21fee72 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -37,6 +37,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -85,6 +86,7 @@
 import android.view.InputChannel;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowInsets;
@@ -952,6 +954,57 @@
     }
 
     @Test
+    public void testDrawMagnifiedViewport() {
+        final int displayId = mDisplayContent.mDisplayId;
+        // Use real surface, so ViewportWindow's BlastBufferQueue can be created.
+        final ArrayList<SurfaceControl> surfaceControls = new ArrayList<>();
+        mWm.mSurfaceControlFactory = s -> new SurfaceControl.Builder() {
+            @Override
+            public SurfaceControl build() {
+                final SurfaceControl sc = super.build();
+                surfaceControls.add(sc);
+                return sc;
+            }
+        };
+        mWm.mAccessibilityController.setMagnificationCallbacks(displayId,
+                mock(WindowManagerInternal.MagnificationCallbacks.class));
+        final boolean[] lockCanvasInWmLock = { false };
+        final Surface surface = mWm.mAccessibilityController.forceShowMagnifierSurface(displayId);
+        spyOn(surface);
+        doAnswer(invocationOnMock -> {
+            lockCanvasInWmLock[0] |= Thread.holdsLock(mWm.mGlobalLock);
+            invocationOnMock.callRealMethod();
+            return null;
+        }).when(surface).lockCanvas(any());
+        mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction);
+        waitUntilHandlersIdle();
+        try {
+            verify(surface).lockCanvas(any());
+
+            clearInvocations(surface);
+            // Invalidate and redraw.
+            mWm.mAccessibilityController.onDisplaySizeChanged(mDisplayContent);
+            mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction);
+            // Turn off magnification to release surface.
+            mWm.mAccessibilityController.setMagnificationCallbacks(displayId, null);
+            if (!com.android.window.flags.Flags.drawMagnifierBorderOutsideWmlock()) {
+                verify(surface).release();
+                assertTrue(lockCanvasInWmLock[0]);
+                return;
+            }
+            waitUntilHandlersIdle();
+            // lockCanvas must not be called after releasing.
+            verify(surface, never()).lockCanvas(any());
+            verify(surface).release();
+            assertFalse(lockCanvasInWmLock[0]);
+        } finally {
+            for (int i = surfaceControls.size() - 1; i >= 0; --i) {
+                surfaceControls.get(i).release();
+            }
+        }
+    }
+
+    @Test
     public void testRequestKeyboardShortcuts_noWindow() {
         doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString());
         doReturn(null).when(mWm).getFocusedWindowLocked();
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index 9c33576..bbd4567 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -36,12 +36,12 @@
 import android.view.InputDevice
 import android.view.inputmethod.InputMethodInfo
 import android.view.inputmethod.InputMethodSubtype
-import androidx.test.core.R
 import androidx.test.core.app.ApplicationProvider
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.internal.os.KeyboardConfiguredProto
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.test.input.R
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals