Merge "Add confirmation dialog for unarchival if app only possesses weak permissions." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index ce3e985..b54022b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -64,6 +64,7 @@
     ":android.hardware.usb.flags-aconfig-java{.generated_srcjars}",
     ":android.tracing.flags-aconfig-java{.generated_srcjars}",
     ":android.appwidget.flags-aconfig-java{.generated_srcjars}",
+    ":android.webkit.flags-aconfig-java{.generated_srcjars}",
 ]
 
 filegroup {
@@ -762,3 +763,19 @@
     aconfig_declarations: "android.appwidget.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// WebView
+aconfig_declarations {
+    name: "android.webkit.flags-aconfig",
+    package: "android.webkit",
+    srcs: [
+        "core/java/android/webkit/*.aconfig",
+        "services/core/java/com/android/server/webkit/*.aconfig",
+    ],
+}
+
+java_aconfig_library {
+    name: "android.webkit.flags-aconfig-java",
+    aconfig_declarations: "android.webkit.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 03a23ba..ca73378 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -32,7 +32,6 @@
     cmd: "$(location hoststubgen) " +
         "@$(location ravenwood/ravenwood-standard-options.txt) " +
 
-        "--out-stub-jar $(location ravenwood_stub.jar) " +
         "--out-impl-jar $(location ravenwood.jar) " +
 
         "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " +
@@ -49,7 +48,6 @@
     ],
     out: [
         "ravenwood.jar",
-        "ravenwood_stub.jar", // It's not used. TODO: Update hoststubgen to make it optional.
 
         // Following files are created just as FYI.
         "hoststubgen_keep_all.txt",
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index 9366ff2d..e1b3241 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -66,6 +66,7 @@
     errorprone: {
         javacflags: [
             "-Xep:ReturnValueIgnored:WARN",
+            "-Xep:UnnecessaryStringBuilder:OFF",
         ],
     },
 }
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index c42c7ca..2ace602 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -128,6 +128,7 @@
         final MergedConfiguration mOutMergedConfiguration = new MergedConfiguration();
         final InsetsState mOutInsetsState = new InsetsState();
         final InsetsSourceControl.Array mOutControls = new InsetsSourceControl.Array();
+        final Bundle mOutBundle = new Bundle();
         final IWindow mWindow;
         final View mView;
         final WindowManager.LayoutParams mParams;
@@ -136,7 +137,7 @@
         final SurfaceControl mOutSurfaceControl;
 
         final IntSupplier mViewVisibility;
-
+        int mRelayoutSeq;
         int mFlags;
 
         RelayoutRunner(Activity activity, IWindow window, IntSupplier visibilitySupplier) {
@@ -152,10 +153,11 @@
         void runBenchmark(BenchmarkState state) throws RemoteException {
             final IWindowSession session = WindowManagerGlobal.getWindowSession();
             while (state.keepRunning()) {
+                mRelayoutSeq++;
                 session.relayout(mWindow, mParams, mWidth, mHeight,
-                        mViewVisibility.getAsInt(), mFlags, 0 /* seq */, 0 /* lastSyncSeqId */,
+                        mViewVisibility.getAsInt(), mFlags, mRelayoutSeq, 0 /* lastSyncSeqId */,
                         mOutFrames, mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState,
-                        mOutControls, new Bundle());
+                        mOutControls, mOutBundle);
             }
         }
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index b828f39..13bea6b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -598,7 +598,6 @@
             long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs,
             int internalFlags,
             int dynamicConstraints) {
-        this.job = job;
         this.callingUid = callingUid;
         this.standbyBucket = standbyBucket;
         mNamespace = namespace;
@@ -626,6 +625,22 @@
             this.sourceTag = tag;
         }
 
+        // This needs to be done before setting the field variable.
+        if (job.getRequiredNetwork() != null) {
+            // Later, when we check if a given network satisfies the required
+            // network, we need to know the UID that is requesting it, so push
+            // the source UID into place.
+            final JobInfo.Builder builder = new JobInfo.Builder(job);
+            builder.setRequiredNetwork(new NetworkRequest.Builder(job.getRequiredNetwork())
+                    .setUids(Collections.singleton(new Range<>(this.sourceUid, this.sourceUid)))
+                    .build());
+            // Don't perform validation checks at this point since we've already passed the
+            // initial validation check.
+            job = builder.build(false, false);
+        }
+
+        this.job = job;
+
         final String bnNamespace = namespace == null ? "" :  "@" + namespace + "@";
         this.batteryName = this.sourceTag != null
                 ? bnNamespace + this.sourceTag + ":" + job.getService().getPackageName()
@@ -708,21 +723,6 @@
 
         updateNetworkBytesLocked();
 
-        if (job.getRequiredNetwork() != null) {
-            // Later, when we check if a given network satisfies the required
-            // network, we need to know the UID that is requesting it, so push
-            // our source UID into place.
-            final JobInfo.Builder builder = new JobInfo.Builder(job);
-            final NetworkRequest.Builder requestBuilder =
-                    new NetworkRequest.Builder(job.getRequiredNetwork());
-            requestBuilder.setUids(
-                    Collections.singleton(new Range<Integer>(this.sourceUid, this.sourceUid)));
-            builder.setRequiredNetwork(requestBuilder.build());
-            // Don't perform validation checks at this point since we've already passed the
-            // initial validation check.
-            job = builder.build(false, false);
-        }
-
         updateMediaBackupExemptionStatus();
     }
 
diff --git a/api/Android.bp b/api/Android.bp
index d6c14fb..2b1cfcb 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -404,3 +404,49 @@
     "ApiDocs.bp",
     "StubLibraries.bp",
 ]
+
+genrule_defaults {
+    name: "flag-api-mapping-generation-defaults",
+    cmd: "$(location extract-flagged-apis) $(in) $(out)",
+    tools: ["extract-flagged-apis"],
+}
+
+genrule {
+    name: "flag-api-mapping-PublicApi",
+    defaults: ["flag-api-mapping-generation-defaults"],
+    srcs: [":frameworks-base-api-current.txt"],
+    out: ["flag_api_map.textproto"],
+    dist: {
+        targets: ["droid"],
+    },
+}
+
+genrule {
+    name: "flag-api-mapping-SystemApi",
+    defaults: ["flag-api-mapping-generation-defaults"],
+    srcs: [":frameworks-base-api-system-current.txt"],
+    out: ["system_flag_api_map.textproto"],
+    dist: {
+        targets: ["droid"],
+    },
+}
+
+genrule {
+    name: "flag-api-mapping-ModuleLibApi",
+    defaults: ["flag-api-mapping-generation-defaults"],
+    srcs: [":frameworks-base-api-module-lib-current.txt"],
+    out: ["module_lib_flag_api_map.textproto"],
+    dist: {
+        targets: ["droid"],
+    },
+}
+
+genrule {
+    name: "flag-api-mapping-SystemServerApi",
+    defaults: ["flag-api-mapping-generation-defaults"],
+    srcs: [":frameworks-base-api-system-server-current.txt"],
+    out: ["system_server_flag_api_map.textproto"],
+    dist: {
+        targets: ["droid"],
+    },
+}
diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt
index 948e64f..9ffb704 100644
--- a/api/coverage/tools/ExtractFlaggedApis.kt
+++ b/api/coverage/tools/ExtractFlaggedApis.kt
@@ -25,7 +25,7 @@
     var cb = ApiFile.parseApi(listOf(File(args[0])))
     val flagToApi = mutableMapOf<String, MutableList<String>>()
     cb.getPackages()
-        .allTopLevelClasses()
+        .allClasses()
         .filter { it.methods().size > 0 }
         .forEach {
             for (method in it.methods()) {
diff --git a/cmds/gpu_counter_producer/Android.bp b/cmds/gpu_counter_producer/Android.bp
index 2232345..d645d06 100644
--- a/cmds/gpu_counter_producer/Android.bp
+++ b/cmds/gpu_counter_producer/Android.bp
@@ -19,6 +19,4 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
-
-    soc_specific: true,
 }
diff --git a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
new file mode 100644
index 0000000..b89e2cd
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright 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.commands.uinput;
+
+import android.annotation.Nullable;
+import android.util.SparseArray;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+
+import src.com.android.commands.uinput.InputAbsInfo;
+
+/**
+ * Parser for the <a href="https://gitlab.freedesktop.org/libevdev/evemu">FreeDesktop evemu</a>
+ * event recording format.
+ */
+public class EvemuParser implements EventParser {
+    private static final String TAG = "UinputEvemuParser";
+
+    /**
+     * The device ID to use for all events. Since evemu files only support single-device
+     * recordings, this will always be the same.
+     */
+    private static final int DEVICE_ID = 1;
+    private static final int REGISTRATION_DELAY_MILLIS = 500;
+
+    private static class CommentAwareReader {
+        private final BufferedReader mReader;
+        private String mNextLine;
+
+        CommentAwareReader(BufferedReader in) throws IOException {
+            mReader = in;
+            mNextLine = findNextLine();
+        }
+
+        private @Nullable String findNextLine() throws IOException {
+            String line = "";
+            while (line != null && line.length() == 0) {
+                String unstrippedLine = mReader.readLine();
+                if (unstrippedLine == null) {
+                    // End of file.
+                    return null;
+                }
+                line = stripComments(unstrippedLine);
+            }
+            return line;
+        }
+
+        private static String stripComments(String line) {
+            int index = line.indexOf('#');
+            // 'N:' lines (which contain the name of the input device) do not support trailing
+            // comments, to support recording device names that contain #s.
+            if (index < 0 || line.startsWith("N: ")) {
+                return line;
+            } else {
+                return line.substring(0, index).strip();
+            }
+        }
+
+        /**
+         * Returns the next line of the file that isn't blank when stripped of comments, or
+         * {@code null} if the end of the file is reached. However, it does not advance to the
+         * next line of the file.
+         */
+        public @Nullable String peekLine() {
+            return mNextLine;
+        }
+
+        /** Moves to the next line of the file. */
+        public void advance() throws IOException {
+            mNextLine = findNextLine();
+        }
+
+        public boolean isAtEndOfFile() {
+            return mNextLine == null;
+        }
+    }
+
+    private final CommentAwareReader mReader;
+    /**
+     * The timestamp of the last event returned, of the head of {@link #mQueuedEvents} if there is
+     * one, or -1 if no events have been returned yet.
+     */
+    private long mLastEventTimeMicros = -1;
+    private final Queue<Event> mQueuedEvents = new ArrayDeque<>(2);
+
+    public EvemuParser(Reader in) throws IOException {
+        mReader = new CommentAwareReader(new BufferedReader(in));
+        mQueuedEvents.add(parseRegistrationEvent());
+
+        // The kernel takes a little time to set up an evdev device after the initial
+        // registration. Any events that we try to inject during this period would be silently
+        // dropped, so we delay for a short period after registration and before injecting any
+        // events.
+        final Event.Builder delayEb = new Event.Builder();
+        delayEb.setId(DEVICE_ID);
+        delayEb.setCommand(Event.Command.DELAY);
+        delayEb.setDurationMillis(REGISTRATION_DELAY_MILLIS);
+        mQueuedEvents.add(delayEb.build());
+    }
+
+    /**
+     * Returns the next event in the evemu recording.
+     */
+    public Event getNextEvent() throws IOException {
+        if (!mQueuedEvents.isEmpty()) {
+            return mQueuedEvents.remove();
+        }
+
+        if (mReader.isAtEndOfFile()) {
+            return null;
+        }
+
+        final String[] parts = expectLineWithParts("E", 4);
+        final String[] timeParts = parts[0].split("\\.");
+        if (timeParts.length != 2) {
+            throw new RuntimeException("Invalid timestamp (does not contain a '.')");
+        }
+        // TODO(b/310958309): use timeMicros to set the timestamp on the event being sent.
+        final long timeMicros =
+                Long.parseLong(timeParts[0]) * 1_000_000 + Integer.parseInt(timeParts[1]);
+        final Event.Builder eb = new Event.Builder();
+        eb.setId(DEVICE_ID);
+        eb.setCommand(Event.Command.INJECT);
+        final int eventType = Integer.parseInt(parts[1], 16);
+        final int eventCode = Integer.parseInt(parts[2], 16);
+        final int value = Integer.parseInt(parts[3]);
+        eb.setInjections(new int[] {eventType, eventCode, value});
+
+        if (mLastEventTimeMicros == -1) {
+            // This is the first event being injected, so send it straight away.
+            mLastEventTimeMicros = timeMicros;
+            return eb.build();
+        } else {
+            final long delayMicros = timeMicros - mLastEventTimeMicros;
+            // The shortest delay supported by Handler.sendMessageAtTime (used for timings by the
+            // Device class) is 1ms, so ignore time differences smaller than that.
+            if (delayMicros < 1000) {
+                mLastEventTimeMicros = timeMicros;
+                return eb.build();
+            } else {
+                // Send a delay now, and queue the actual event for the next call.
+                mQueuedEvents.add(eb.build());
+                mLastEventTimeMicros = timeMicros;
+                final Event.Builder delayEb = new Event.Builder();
+                delayEb.setId(DEVICE_ID);
+                delayEb.setCommand(Event.Command.DELAY);
+                delayEb.setDurationMillis((int) (delayMicros / 1000));
+                return delayEb.build();
+            }
+        }
+    }
+
+    private Event parseRegistrationEvent() throws IOException {
+        // The registration details at the start of a recording are specified by a set of lines
+        // that have to be in this order: N, I, P, B, A, L, S. Recordings must have exactly one N
+        // (name) and I (IDs) line. The remaining lines are optional, and there may be multiple
+        // of those lines.
+
+        final Event.Builder eb = new Event.Builder();
+        eb.setId(DEVICE_ID);
+        eb.setCommand(Event.Command.REGISTER);
+        eb.setName(expectLine("N"));
+
+        final String[] idStrings = expectLineWithParts("I", 4);
+        eb.setBusId(Integer.parseInt(idStrings[0], 16));
+        eb.setVid(Integer.parseInt(idStrings[1], 16));
+        eb.setPid(Integer.parseInt(idStrings[2], 16));
+        // TODO(b/302297266): support setting the version ID, and set it to idStrings[3].
+
+        final SparseArray<int[]> config = new SparseArray<>();
+        config.append(Event.UinputControlCode.UI_SET_PROPBIT.getValue(), parseProperties());
+
+        parseAxisBitmaps(config);
+
+        eb.setConfiguration(config);
+        if (config.contains(Event.UinputControlCode.UI_SET_FFBIT.getValue())) {
+            // If the device specifies any force feedback effects, the kernel will require the
+            // ff_effects_max value to be set.
+            eb.setFfEffectsMax(config.get(Event.UinputControlCode.UI_SET_FFBIT.getValue()).length);
+        }
+
+        eb.setAbsInfo(parseAbsInfos());
+
+        // L: and S: lines allow the initial states of the device's LEDs and switches to be
+        // recorded. However, the FreeDesktop implementation doesn't support actually setting these
+        // states at the start of playback (apparently due to concerns over race conditions), and we
+        // have no need for this feature either, so for now just skip over them.
+        skipUnsupportedLines("L");
+        skipUnsupportedLines("S");
+
+        return eb.build();
+    }
+
+    private int[] parseProperties() throws IOException {
+        final List<String> propBitmapParts = new ArrayList<>();
+        String line = acceptLine("P");
+        while (line != null) {
+            propBitmapParts.addAll(List.of(line.strip().split(" ")));
+            line = acceptLine("P");
+        }
+        return hexStringBitmapToEventCodes(propBitmapParts);
+    }
+
+    private void parseAxisBitmaps(SparseArray<int[]> config) throws IOException {
+        final Map<Integer, List<String>> axisBitmapParts = new HashMap<>();
+        String line = acceptLine("B");
+        while (line != null) {
+            final String[] parts = line.strip().split(" ");
+            if (parts.length < 2) {
+                throw new RuntimeException(
+                        "Expected event type and at least one bitmap byte on 'B:' line; only found "
+                                + parts.length + " elements");
+            }
+            final int eventType = Integer.parseInt(parts[0], 16);
+            // EV_SYN cannot be configured through uinput, so skip it.
+            if (eventType != Event.EV_SYN) {
+                if (!axisBitmapParts.containsKey(eventType)) {
+                    axisBitmapParts.put(eventType, new ArrayList<>());
+                }
+                for (int i = 1; i < parts.length; i++) {
+                    axisBitmapParts.get(eventType).add(parts[i]);
+                }
+            }
+            line = acceptLine("B");
+        }
+        final List<Integer> eventTypesToSet = new ArrayList<>();
+        for (var entry : axisBitmapParts.entrySet()) {
+            if (entry.getValue().size() == 0) {
+                continue;
+            }
+            final Event.UinputControlCode controlCode =
+                    Event.UinputControlCode.forEventType(entry.getKey());
+            final int[] eventCodes = hexStringBitmapToEventCodes(entry.getValue());
+            if (controlCode != null && eventCodes.length > 0) {
+                config.append(controlCode.getValue(), eventCodes);
+                eventTypesToSet.add(entry.getKey());
+            }
+        }
+        config.append(
+                Event.UinputControlCode.UI_SET_EVBIT.getValue(), unboxIntList(eventTypesToSet));
+    }
+
+    private SparseArray<InputAbsInfo> parseAbsInfos() throws IOException {
+        final SparseArray<InputAbsInfo> absInfos = new SparseArray<>();
+        String line = acceptLine("A");
+        while (line != null) {
+            final String[] parts = line.strip().split(" ");
+            if (parts.length < 5 || parts.length > 6) {
+                throw new RuntimeException(
+                        "'A:' lines should have the format 'A: <index (hex)> <min> <max> <fuzz> "
+                                + "<flat> [<resolution>]'; expected 5 or 6 numbers but found "
+                                + parts.length);
+            }
+            final int axisCode = Integer.parseInt(parts[0], 16);
+            final InputAbsInfo info = new InputAbsInfo();
+            info.minimum = Integer.parseInt(parts[1]);
+            info.maximum = Integer.parseInt(parts[2]);
+            info.fuzz = Integer.parseInt(parts[3]);
+            info.flat = Integer.parseInt(parts[4]);
+            info.resolution = parts.length > 5 ? Integer.parseInt(parts[5]) : 0;
+            absInfos.append(axisCode, info);
+            line = acceptLine("A");
+        }
+        return absInfos;
+    }
+
+    private void skipUnsupportedLines(String type) throws IOException {
+        if (acceptLine(type) != null) {
+            while (acceptLine(type) != null) {
+                // Skip the line.
+            }
+        }
+    }
+
+    /**
+     * Returns the contents of the next line in the file if it has the given type, or raises an
+     * error if it does not.
+     *
+     * @param type the type of the line to expect, represented by the letter before the ':'.
+     * @return the part of the line after the ": ".
+     */
+    private String expectLine(String type) throws IOException {
+        final String line = acceptLine(type);
+        if (line == null) {
+            throw new RuntimeException("Expected line of type '" + type + "'");
+        } else {
+            return line;
+        }
+    }
+
+    /**
+     * Peeks at the next line in the file to see if it has the given type, and if so, returns its
+     * contents and advances the reader.
+     *
+     * @param type the type of the line to accept, represented by the letter before the ':'.
+     * @return the part of the line after the ": ", if the type matches; otherwise {@code null}.
+     */
+    private @Nullable String acceptLine(String type) throws IOException {
+        final String line = mReader.peekLine();
+        if (line == null) {
+            return null;
+        }
+        final String[] lineParts = line.split(": ", 2);
+        if (lineParts.length < 2) {
+            // TODO(b/302297266): make a proper exception class for syntax errors, including line
+            // numbers, etc.. (We can use LineNumberReader to track them.)
+            throw new RuntimeException("Line without ': '");
+        }
+        if (lineParts[0].equals(type)) {
+            mReader.advance();
+            return lineParts[1];
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Like {@link #expectLine(String)}, but also checks that the contents of the line is formed of
+     * {@code numParts} space-separated parts.
+     *
+     * @param type the type of the line to expect, represented by the letter before the ':'.
+     * @param numParts the number of parts to expect.
+     * @return the part of the line after the ": ", split into {@code numParts} sections.
+     */
+    private String[] expectLineWithParts(String type, int numParts) throws IOException {
+        final String[] parts = expectLine(type).strip().split(" ");
+        if (parts.length != numParts) {
+            throw new RuntimeException("Expected a '" + type + "' line with " + numParts
+                    + " parts, found one with " + parts.length);
+        }
+        return parts;
+    }
+
+    private static int[] hexStringBitmapToEventCodes(List<String> strs) {
+        final List<Integer> codes = new ArrayList<>();
+        for (int iByte = 0; iByte < strs.size(); iByte++) {
+            int b = Integer.parseInt(strs.get(iByte), 16);
+            if (b < 0x0 || b > 0xff) {
+                throw new RuntimeException("Bitmap part '" + strs.get(iByte)
+                        + "' invalid; parts must be between 00 and ff.");
+            }
+            for (int iBit = 0; iBit < 8; iBit++) {
+                if ((b & 1) != 0) {
+                    codes.add(iByte * 8 + iBit);
+                }
+                b >>= 1;
+            }
+        }
+        return unboxIntList(codes);
+    }
+
+    private static int[] unboxIntList(List<Integer> list) {
+        final int[] array = new int[list.size()];
+        Arrays.setAll(array, list::get);
+        return array;
+    }
+}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 4498bc2..5ec40e5 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -16,6 +16,7 @@
 
 package com.android.commands.uinput;
 
+import android.annotation.Nullable;
 import android.util.SparseArray;
 
 import java.util.Arrays;
@@ -39,6 +40,7 @@
 
     // Constants representing evdev event types, from include/uapi/linux/input-event-codes.h in the
     // kernel.
+    public static final int EV_SYN = 0x00;
     public static final int EV_KEY = 0x01;
     public static final int EV_REL = 0x02;
     public static final int EV_ABS = 0x03;
@@ -69,19 +71,23 @@
         public int getValue() {
             return mValue;
         }
-    }
 
-    // These constants come from "include/uapi/linux/input.h" in the kernel
-    enum Bus {
-        USB(0x03), BLUETOOTH(0x05);
-        private final int mValue;
-
-        Bus(int value) {
-            mValue = value;
-        }
-
-        int getValue() {
-            return mValue;
+        /**
+         * Returns the control code for the given evdev event type, or {@code null} if there is no
+         * control code for that type.
+         */
+        public static @Nullable UinputControlCode forEventType(int eventType) {
+            return switch (eventType) {
+                case EV_KEY -> UI_SET_KEYBIT;
+                case EV_REL -> UI_SET_RELBIT;
+                case EV_ABS -> UI_SET_ABSBIT;
+                case EV_MSC -> UI_SET_MSCBIT;
+                case EV_SW -> UI_SET_SWBIT;
+                case EV_LED -> UI_SET_LEDBIT;
+                case EV_SND -> UI_SET_SNDBIT;
+                case EV_FF -> UI_SET_FFBIT;
+                default -> null;
+            };
         }
     }
 
@@ -90,7 +96,7 @@
     private String mName;
     private int mVid;
     private int mPid;
-    private Bus mBus;
+    private int mBusId;
     private int[] mInjections;
     private SparseArray<int[]> mConfiguration;
     private int mDurationMillis;
@@ -120,7 +126,7 @@
     }
 
     public int getBus() {
-        return mBus.getValue();
+        return mBusId;
     }
 
     public int[] getInjections() {
@@ -168,7 +174,7 @@
             + ", name=" + mName
             + ", vid=" + mVid
             + ", pid=" + mPid
-            + ", bus=" + mBus
+            + ", busId=" + mBusId
             + ", events=" + Arrays.toString(mInjections)
             + ", configuration=" + mConfiguration
             + ", duration=" + mDurationMillis + "ms"
@@ -218,8 +224,8 @@
             mEvent.mPid = pid;
         }
 
-        public void setBus(Bus bus) {
-            mEvent.mBus = bus;
+        public void setBusId(int busId) {
+            mEvent.mBusId = busId;
         }
 
         public void setDurationMillis(int durationMillis) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/cmds/uinput/src/com/android/commands/uinput/EventParser.java
similarity index 60%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to cmds/uinput/src/com/android/commands/uinput/EventParser.java
index a780763..a4df03d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/cmds/uinput/src/com/android/commands/uinput/EventParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 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.
@@ -14,8 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.commands.uinput;
 
-import com.android.systemui.kosmos.Kosmos
+import java.io.IOException;
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+/**
+ * Interface for a class that reads a stream of {@link Event}s.
+ */
+public interface EventParser {
+    /**
+     * Returns the next event in the file that the parser is reading from.
+     */
+    Event getNextEvent() throws IOException;
+}
diff --git a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
index a2195c7..888ec5a 100644
--- a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
+++ b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
@@ -22,7 +22,7 @@
 import android.util.SparseArray;
 
 import java.io.IOException;
-import java.io.InputStreamReader;
+import java.io.Reader;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -34,12 +34,12 @@
 /**
  * A class that parses the JSON-like event format described in the README to build {@link Event}s.
  */
-public class JsonStyleParser {
+public class JsonStyleParser implements EventParser {
     private static final String TAG = "UinputJsonStyleParser";
 
     private JsonReader mReader;
 
-    public JsonStyleParser(InputStreamReader in) {
+    public JsonStyleParser(Reader in) {
         mReader = new JsonReader(in);
         mReader.setLenient(true);
     }
@@ -62,7 +62,7 @@
                         case "name" -> eb.setName(mReader.nextString());
                         case "vid" -> eb.setVid(readInt());
                         case "pid" -> eb.setPid(readInt());
-                        case "bus" -> eb.setBus(readBus());
+                        case "bus" -> eb.setBusId(readBus());
                         case "events" -> {
                             int[] injections = readInjectedEvents().stream()
                                     .mapToInt(Integer::intValue).toArray();
@@ -139,9 +139,35 @@
         });
     }
 
-    private Event.Bus readBus() throws IOException {
+    private int readBus() throws IOException {
         String val = mReader.nextString();
-        return Event.Bus.valueOf(val.toUpperCase());
+        // See include/uapi/linux/input.h in the kernel for the source of these constants.
+        return switch (val.toUpperCase()) {
+            case "PCI" -> 0x01;
+            case "ISAPNP" -> 0x02;
+            case "USB" -> 0x03;
+            case "HIL" -> 0x04;
+            case "BLUETOOTH" -> 0x05;
+            case "VIRTUAL" -> 0x06;
+            case "ISA" -> 0x10;
+            case "I8042" -> 0x11;
+            case "XTKBD" -> 0x12;
+            case "RS232" -> 0x13;
+            case "GAMEPORT" -> 0x14;
+            case "PARPORT" -> 0x15;
+            case "AMIGA" -> 0x16;
+            case "ADB" -> 0x17;
+            case "I2C" -> 0x18;
+            case "HOST" -> 0x19;
+            case "GSC" -> 0x1A;
+            case "ATARI" -> 0x1B;
+            case "SPI" -> 0x1C;
+            case "RMI" -> 0x1D;
+            case "CEC" -> 0x1E;
+            case "INTEL_ISHTP" -> 0x1F;
+            case "AMD_SFH" -> 0x20;
+            default -> throw new IllegalArgumentException("Invalid bus ID " + val);
+        };
     }
 
     private SparseArray<int[]> readConfiguration()
diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
index fe76abb..684a12f 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
@@ -19,12 +19,12 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
 import java.util.Objects;
 
 /**
@@ -35,7 +35,7 @@
 public class Uinput {
     private static final String TAG = "UINPUT";
 
-    private final JsonStyleParser mParser;
+    private final EventParser mParser;
     private final SparseArray<Device> mDevices;
 
     private static void usage() {
@@ -74,12 +74,32 @@
     private Uinput(InputStream in) {
         mDevices = new SparseArray<Device>();
         try {
-            mParser = new JsonStyleParser(new InputStreamReader(in, "UTF-8"));
-        } catch (UnsupportedEncodingException e) {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
+            mParser = isEvemuFile(reader) ? new EvemuParser(reader) : new JsonStyleParser(reader);
+        } catch (IOException e) {
             throw new RuntimeException(e);
         }
     }
 
+    private boolean isEvemuFile(BufferedReader in) throws IOException {
+        // After zero or more empty lines (not even containing horizontal whitespace), evemu
+        // recordings must either start with '#' (indicating the EVEMU version header or a comment)
+        // or 'N' (for the name line). If we encounter anything else, assume it's a JSON-style input
+        // file.
+
+        String lineSep = System.lineSeparator();
+        char[] buf = new char[1];
+
+        in.mark(1 /* readAheadLimit */);
+        int charsRead = in.read(buf);
+        while (charsRead > 0 && lineSep.contains(String.valueOf(buf[0]))) {
+            in.mark(1 /* readAheadLimit */);
+            charsRead = in.read(buf);
+        }
+        in.reset();
+        return buf[0] == '#' || buf[0] == 'N';
+    }
+
     private void run() {
         try {
             Event e = null;
diff --git a/core/api/current.txt b/core/api/current.txt
index 207abb2..812d4cd6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12781,6 +12781,7 @@
     method public boolean isPackageSuspended();
     method @CheckResult public abstract boolean isPermissionRevokedByPolicy(@NonNull String, @NonNull String);
     method public abstract boolean isSafeMode();
+    method @FlaggedApi("android.content.pm.get_package_info") @WorkerThread public <T> T parseAndroidManifest(@NonNull String, @NonNull java.util.function.Function<android.content.res.XmlResourceParser,T>) throws java.io.IOException;
     method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryActivityProperty(@NonNull String);
     method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryApplicationProperty(@NonNull String);
     method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int);
@@ -36759,6 +36760,7 @@
     field public static final String ACTION_APP_OPEN_BY_DEFAULT_SETTINGS = "android.settings.APP_OPEN_BY_DEFAULT_SETTINGS";
     field public static final String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";
     field public static final String ACTION_APP_USAGE_SETTINGS = "android.settings.action.APP_USAGE_SETTINGS";
+    field @FlaggedApi("android.app.modes_api") public static final String ACTION_AUTOMATIC_ZEN_RULE_SETTINGS = "android.settings.AUTOMATIC_ZEN_RULE_SETTINGS";
     field public static final String ACTION_AUTO_ROTATE_SETTINGS = "android.settings.AUTO_ROTATE_SETTINGS";
     field public static final String ACTION_BATTERY_SAVER_SETTINGS = "android.settings.BATTERY_SAVER_SETTINGS";
     field public static final String ACTION_BIOMETRIC_ENROLL = "android.settings.BIOMETRIC_ENROLL";
@@ -36846,6 +36848,7 @@
     field public static final String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled";
     field public static final String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE";
     field public static final String EXTRA_AUTHORITIES = "authorities";
+    field @FlaggedApi("android.app.modes_api") public static final String EXTRA_AUTOMATIC_ZEN_RULE_ID = "android.provider.extra.AUTOMATIC_ZEN_RULE_ID";
     field public static final String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
     field public static final String EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED = "android.provider.extra.BIOMETRIC_AUTHENTICATORS_ALLOWED";
     field public static final String EXTRA_CHANNEL_FILTER_LIST = "android.provider.extra.CHANNEL_FILTER_LIST";
@@ -45159,6 +45162,7 @@
     method public void addOnSubscriptionsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void addSubscriptionsIntoGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
     method public boolean canManageSubscription(android.telephony.SubscriptionInfo);
+    method @FlaggedApi("com.android.internal.telephony.flags.work_profile_api_split") @NonNull public android.telephony.SubscriptionManager createForAllUserProfiles();
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>);
     method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context);
     method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
@@ -54183,7 +54187,7 @@
     method public boolean isEnabled();
     method public boolean isFocusable();
     method public boolean isFocused();
-    method public boolean isGranularScrollingSupported();
+    method @FlaggedApi("android.view.accessibility.granular_scrolling") public boolean isGranularScrollingSupported();
     method public boolean isHeading();
     method public boolean isImportantForAccessibility();
     method public boolean isLongClickable();
@@ -54233,7 +54237,7 @@
     method public void setError(CharSequence);
     method public void setFocusable(boolean);
     method public void setFocused(boolean);
-    method public void setGranularScrollingSupported(boolean);
+    method @FlaggedApi("android.view.accessibility.granular_scrolling") public void setGranularScrollingSupported(boolean);
     method public void setHeading(boolean);
     method public void setHintText(CharSequence);
     method public void setImportantForAccessibility(boolean);
@@ -54288,7 +54292,7 @@
     field public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
     field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
     field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
-    field public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
+    field @FlaggedApi("android.view.accessibility.granular_scrolling") public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
     field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
     field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
     field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
@@ -54391,10 +54395,9 @@
   public static final class AccessibilityNodeInfo.CollectionInfo {
     ctor public AccessibilityNodeInfo.CollectionInfo(int, int, boolean);
     ctor public AccessibilityNodeInfo.CollectionInfo(int, int, boolean, int);
-    ctor public AccessibilityNodeInfo.CollectionInfo(int, int, boolean, int, int, int);
     method public int getColumnCount();
-    method public int getImportantForAccessibilityItemCount();
-    method public int getItemCount();
+    method @FlaggedApi("android.view.accessibility.collection_info_item_counts") public int getImportantForAccessibilityItemCount();
+    method @FlaggedApi("android.view.accessibility.collection_info_item_counts") public int getItemCount();
     method public int getRowCount();
     method public int getSelectionMode();
     method public boolean isHierarchical();
@@ -54403,18 +54406,18 @@
     field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
     field public static final int SELECTION_MODE_NONE = 0; // 0x0
     field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
-    field public static final int UNDEFINED = -1; // 0xffffffff
+    field @FlaggedApi("android.view.accessibility.collection_info_item_counts") public static final int UNDEFINED = -1; // 0xffffffff
   }
 
-  public static final class AccessibilityNodeInfo.CollectionInfo.Builder {
-    ctor public AccessibilityNodeInfo.CollectionInfo.Builder();
-    method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo build();
-    method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setColumnCount(int);
-    method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setHierarchical(boolean);
-    method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setImportantForAccessibilityItemCount(int);
-    method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setItemCount(int);
-    method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setRowCount(int);
-    method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setSelectionMode(int);
+  @FlaggedApi("android.view.accessibility.collection_info_item_counts") public static final class AccessibilityNodeInfo.CollectionInfo.Builder {
+    ctor @FlaggedApi("android.view.accessibility.collection_info_item_counts") public AccessibilityNodeInfo.CollectionInfo.Builder();
+    method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo build();
+    method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setColumnCount(int);
+    method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setHierarchical(boolean);
+    method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setImportantForAccessibilityItemCount(int);
+    method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setItemCount(int);
+    method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setRowCount(int);
+    method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setSelectionMode(int);
   }
 
   public static final class AccessibilityNodeInfo.CollectionItemInfo {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 275fe77..c282e4b6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -307,7 +307,7 @@
     field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";
     field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
     field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
-    field public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE";
+    field @FlaggedApi("com.android.net.flags.register_nsd_offload_engine") public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE";
     field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
     field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM";
     field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index bb335fa..6c10f49 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.DETECT_SCREEN_CAPTURE;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.inMultiWindowMode;
 import static android.os.Process.myUid;
@@ -7603,15 +7604,17 @@
      * @param taskDescription The TaskDescription properties that describe the task with this activity
      */
     public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
-        if (mTaskDescription != taskDescription) {
-            mTaskDescription.copyFromPreserveHiddenFields(taskDescription);
-            // Scale the icon down to something reasonable if it is provided
-            if (taskDescription.getIconFilename() == null && taskDescription.getIcon() != null) {
-                final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
-                final Bitmap icon = Bitmap.createScaledBitmap(taskDescription.getIcon(), size, size,
-                        true);
-                mTaskDescription.setIcon(Icon.createWithBitmap(icon));
-            }
+        if (taskDescription == null || mTaskDescription.equals(taskDescription)) {
+            return;
+        }
+
+        mTaskDescription.copyFromPreserveHiddenFields(taskDescription);
+        // Scale the icon down to something reasonable if it is provided
+        if (taskDescription.getIconFilename() == null && taskDescription.getIcon() != null) {
+            final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
+            final Bitmap icon = Bitmap.createScaledBitmap(taskDescription.getIcon(), size, size,
+                    true);
+            mTaskDescription.setIcon(Icon.createWithBitmap(icon));
         }
         ActivityClient.getInstance().setTaskDescription(mToken, mTaskDescription);
     }
@@ -9439,6 +9442,15 @@
         ActivityClient.getInstance().enableTaskLocaleOverride(mToken);
     }
 
+    /**
+     * Request ActivityRecordInputSink to enable or disable blocking input events.
+     * @hide
+     */
+    @RequiresPermission(INTERNAL_SYSTEM_WINDOW)
+    public void setActivityRecordInputSinkEnabled(boolean enabled) {
+        ActivityClient.getInstance().setActivityRecordInputSinkEnabled(mToken, enabled);
+    }
+
     class HostCallbacks extends FragmentHostCallback<Activity> {
         public HostCallbacks() {
             super(Activity.this /*activity*/);
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index b35e87b..b8bd030 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.content.ComponentName;
@@ -614,6 +616,15 @@
         }
     }
 
+    @RequiresPermission(INTERNAL_SYSTEM_WINDOW)
+    void setActivityRecordInputSinkEnabled(IBinder activityToken, boolean enabled) {
+        try {
+            getActivityClientController().setActivityRecordInputSinkEnabled(activityToken, enabled);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Shows or hides a Camera app compat toggle for stretched issues with the requested state.
      *
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 02eaf0b..8af1216 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -232,6 +232,7 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.org.conscrypt.TrustedCertificateStore;
 import com.android.server.am.MemInfoDumpProto;
+import com.android.window.flags.Flags;
 
 import dalvik.annotation.optimization.NeverCompile;
 import dalvik.system.AppSpecializationHooks;
@@ -3713,7 +3714,13 @@
         final ArrayList<ResultInfo> list = new ArrayList<>();
         list.add(new ResultInfo(id, requestCode, resultCode, data));
         final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread);
-        clientTransaction.addCallback(ActivityResultItem.obtain(activityToken, list));
+        final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
+                activityToken, list);
+        if (Flags.bundleClientTransactionFlag()) {
+            clientTransaction.addTransactionItem(activityResultItem);
+        } else {
+            clientTransaction.addCallback(activityResultItem);
+        }
         try {
             mAppThread.scheduleTransaction(clientTransaction);
         } catch (RemoteException e) {
@@ -4492,16 +4499,26 @@
 
     private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) {
         final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
-        transaction.setLifecycleStateRequest(PauseActivityItem.obtain(r.token,
+        final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token,
                 r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags,
-                /* dontReport */ false, /* autoEnteringPip */ false));
+                /* dontReport */ false, /* autoEnteringPip */ false);
+        if (Flags.bundleClientTransactionFlag()) {
+            transaction.addTransactionItem(pauseActivityItem);
+        } else {
+            transaction.setLifecycleStateRequest(pauseActivityItem);
+        }
         executeTransaction(transaction);
     }
 
     private void scheduleResume(ActivityClientRecord r) {
         final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
-        transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(r.token,
-                /* isForward */ false, /* shouldSendCompatFakeFocus */ false));
+        final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token,
+                /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
+        if (Flags.bundleClientTransactionFlag()) {
+            transaction.addTransactionItem(resumeActivityItem);
+        } else {
+            transaction.setLifecycleStateRequest(resumeActivityItem);
+        }
         executeTransaction(transaction);
     }
 
@@ -6092,8 +6109,13 @@
                 TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
         // Schedule the transaction.
         final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
-        transaction.addCallback(activityRelaunchItem);
-        transaction.setLifecycleStateRequest(lifecycleRequest);
+        if (Flags.bundleClientTransactionFlag()) {
+            transaction.addTransactionItem(activityRelaunchItem);
+            transaction.addTransactionItem(lifecycleRequest);
+        } else {
+            transaction.addCallback(activityRelaunchItem);
+            transaction.setLifecycleStateRequest(lifecycleRequest);
+        }
         executeTransaction(transaction);
     }
 
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index ca6d8df..a4c3bb8 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -80,6 +80,8 @@
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
 import android.content.pm.dex.ArtManager;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.res.ApkAssets;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
@@ -144,6 +146,7 @@
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /** @hide */
 public class ApplicationPackageManager extends PackageManager {
@@ -4024,4 +4027,34 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    @Override
+    public <T> T parseAndroidManifest(@NonNull String apkFilePath,
+            @NonNull Function<XmlResourceParser, T> parserFunction) throws IOException {
+        Objects.requireNonNull(apkFilePath, "apkFilePath cannot be null");
+        Objects.requireNonNull(parserFunction, "parserFunction cannot be null");
+        try (XmlResourceParser xmlResourceParser = getAndroidManifestParser(apkFilePath)) {
+            return parserFunction.apply(xmlResourceParser);
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to get the android manifest parser", e);
+            throw e;
+        }
+    }
+
+    private static XmlResourceParser getAndroidManifestParser(@NonNull String apkFilePath)
+            throws IOException {
+        ApkAssets apkAssets = null;
+        try {
+            apkAssets = ApkAssets.loadFromPath(apkFilePath);
+            return apkAssets.openXml(ApkLiteParseUtils.ANDROID_MANIFEST_FILENAME);
+        } finally {
+            if (apkAssets != null) {
+                try {
+                    apkAssets.close();
+                } catch (Throwable ignored) {
+                    Log.w(TAG, "Failed to close apkAssets", ignored);
+                }
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index a3c5e1c..7370fc3 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -191,4 +191,14 @@
      */
     boolean isRequestedToLaunchInTaskFragment(in IBinder activityToken,
             in IBinder taskFragmentToken);
+
+    /**
+     * Enable or disable ActivityRecordInputSink to block input events.
+     *
+     * @param token The token for the activity that requests to toggle.
+     * @param enabled Whether the input evens are blocked by ActivityRecordInputSink.
+     */
+    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+            + ".permission.INTERNAL_SYSTEM_WINDOW)")
+    oneway void setActivityRecordInputSinkEnabled(in IBinder activityToken, boolean enabled);
 }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 56d0d1f..6c9c14f 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -51,6 +51,7 @@
 import android.os.ServiceManager;
 import android.os.StrictMode;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.service.notification.Adjustment;
 import android.service.notification.Condition;
@@ -1253,7 +1254,8 @@
      * <p>
      * If this method returns true, calls to
      * {@link #updateAutomaticZenRule(String, AutomaticZenRule)} may fail and apps should defer
-     * rule management to system settings/uis.
+     * rule management to system settings/uis via
+     * {@link Settings#ACTION_AUTOMATIC_ZEN_RULE_SETTINGS}.
      */
     @FlaggedApi(Flags.FLAG_MODES_API)
     public boolean areAutomaticZenRulesUserManaged() {
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index cc56a1c..d8448dc 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -72,6 +72,7 @@
 per-file *Zen* = file:/packages/SystemUI/OWNERS
 per-file *StatusBar* = file:/packages/SystemUI/OWNERS
 per-file *UiModeManager* = file:/packages/SystemUI/OWNERS
+per-file notification.aconfig = file:/packages/SystemUI/OWNERS
 
 # PackageManager
 per-file ApplicationPackageManager.java = file:/services/core/java/com/android/server/pm/OWNERS
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index bbd07b8..35ce102 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -48,3 +48,10 @@
   description: "Update DumpSys to include information about migrated APIs in DPE"
   bug: "304999634"
 }
+
+flag {
+  name: "quiet_mode_credential_bug_fix"
+  namespace: "enterprise"
+  description: "Guards a bugfix that ends the credential input flow if the managed user has not stopped."
+  bug: "293441361"
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 7c34cde..5e55268 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -75,7 +75,6 @@
     /**
      * Adds a message to the end of the sequence of transaction items.
      * @param item A single message that can contain a client activity/window request/callback.
-     * TODO(b/260873529): replace both {@link #addCallback} and {@link #setLifecycleStateRequest}.
      */
     public void addTransactionItem(@NonNull ClientTransactionItem item) {
         if (mTransactionItems == null) {
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index dfbccb4..475c6fb 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -235,7 +235,9 @@
      *   Configuration - ActivityResult - Configuration - ActivityResult
      * index 1 will be returned, because ActivityResult request on position 1 will be the last
      * request that moves activity to the RESUMED state where it will eventually end.
+     * @deprecated to be removed with {@link TransactionExecutor#executeCallbacks}.
      */
+    @Deprecated
     static int lastCallbackRequestingState(@NonNull ClientTransaction transaction) {
         final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
         if (callbacks == null || callbacks.isEmpty()
diff --git a/core/java/android/app/time/TEST_MAPPING b/core/java/android/app/time/TEST_MAPPING
index 49a4467..47a152a 100644
--- a/core/java/android/app/time/TEST_MAPPING
+++ b/core/java/android/app/time/TEST_MAPPING
@@ -1,6 +1,5 @@
 {
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FrameworksTimeCoreTests",
       "options": [
@@ -8,7 +7,10 @@
           "include-filter": "android.app."
         }
       ]
-    },
+    }
+  ],
+  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+  "postsubmit": [
     {
       "name": "FrameworksServicesTests",
       "options": [
diff --git a/core/java/android/app/timedetector/TEST_MAPPING b/core/java/android/app/timedetector/TEST_MAPPING
index c050a55..9517fb9 100644
--- a/core/java/android/app/timedetector/TEST_MAPPING
+++ b/core/java/android/app/timedetector/TEST_MAPPING
@@ -1,6 +1,5 @@
 {
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FrameworksTimeCoreTests",
       "options": [
@@ -8,7 +7,10 @@
           "include-filter": "android.app."
         }
       ]
-    },
+    }
+  ],
+  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+  "postsubmit": [
     {
       "name": "FrameworksServicesTests",
       "options": [
diff --git a/core/java/android/app/timezonedetector/TEST_MAPPING b/core/java/android/app/timezonedetector/TEST_MAPPING
index 46656d1..fd41b86 100644
--- a/core/java/android/app/timezonedetector/TEST_MAPPING
+++ b/core/java/android/app/timezonedetector/TEST_MAPPING
@@ -1,6 +1,5 @@
 {
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FrameworksTimeCoreTests",
       "options": [
@@ -8,7 +7,10 @@
           "include-filter": "android.app."
         }
       ]
-    },
+    }
+  ],
+  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+  "postsubmit": [
     {
       "name": "FrameworksServicesTests",
       "options": [
diff --git a/core/java/android/app/usage/ParcelableUsageEventList.java b/core/java/android/app/usage/ParcelableUsageEventList.java
index 016d97f..aa32392 100644
--- a/core/java/android/app/usage/ParcelableUsageEventList.java
+++ b/core/java/android/app/usage/ParcelableUsageEventList.java
@@ -48,13 +48,16 @@
 
     private List<Event> mList;
 
-    public ParcelableUsageEventList(List<Event> list) {
+    public ParcelableUsageEventList(@NonNull List<Event> list) {
+        if (list == null) {
+            throw new IllegalArgumentException("Empty list");
+        }
         mList = list;
     }
 
     private ParcelableUsageEventList(Parcel in) {
         final int N = in.readInt();
-        mList = new ArrayList<>();
+        mList = new ArrayList<>(N);
         if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
         if (N <= 0) {
             return;
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 1eb452c..6c7eba0 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -24,9 +24,11 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -35,6 +37,7 @@
  * from which to read {@link android.app.usage.UsageEvents.Event} objects.
  */
 public final class UsageEvents implements Parcelable {
+    private static final String TAG = "UsageEvents";
 
     /** @hide */
     public static final String INSTANT_APP_PACKAGE_NAME = "android.instant_app";
@@ -786,10 +789,20 @@
     }
 
     private void readUsageEventsFromParcelWithParceledList(Parcel in) {
+        mEventCount = in.readInt();
         mIndex = in.readInt();
-        mEventsToWrite = in.readParcelable(UsageEvents.class.getClassLoader(),
-            ParcelableUsageEventList.class).getList();
-        mEventCount = mEventsToWrite.size();
+        ParcelableUsageEventList slice = in.readParcelable(getClass().getClassLoader(),
+                ParcelableUsageEventList.class);
+        if (slice != null) {
+            mEventsToWrite = slice.getList();
+        } else {
+            mEventsToWrite = new ArrayList<>();
+        }
+
+        if (mEventCount != mEventsToWrite.size()) {
+            Log.w(TAG, "Partial usage event list received: " + mEventCount + " != "
+                    + mEventsToWrite.size());
+        }
     }
 
     private void readUsageEventsFromParcelWithBlob(Parcel in) {
@@ -1065,6 +1078,7 @@
     }
 
     private void writeUsageEventsToParcelWithParceledList(Parcel dest, int flags) {
+        dest.writeInt(mEventCount);
         dest.writeInt(mIndex);
         dest.writeParcelable(new ParcelableUsageEventList(mEventsToWrite), flags);
     }
diff --git a/core/java/android/companion/utils/FeatureUtils.java b/core/java/android/companion/utils/FeatureUtils.java
deleted file mode 100644
index a382e09..0000000
--- a/core/java/android/companion/utils/FeatureUtils.java
+++ /dev/null
@@ -1,52 +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 android.companion.utils;
-
-import android.os.Binder;
-import android.os.Build;
-import android.provider.DeviceConfig;
-
-/**
- * Util class for feature flags
- *
- * @hide
- */
-public final class FeatureUtils {
-
-    private static final String NAMESPACE_COMPANION = "companion";
-
-    private static final String PROPERTY_PERM_SYNC_ENABLED = "perm_sync_enabled";
-
-    public static boolean isPermSyncEnabled() {
-        // Permissions sync is always enabled in debuggable mode.
-        if (Build.isDebuggable()) {
-            return true;
-        }
-
-        // Clear app identity to read the device config for feature flag.
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return DeviceConfig.getBoolean(NAMESPACE_COMPANION,
-                    PROPERTY_PERM_SYNC_ENABLED, false);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    private FeatureUtils() {
-    }
-}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c3b3423..fe31c9d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -34,6 +34,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
 import android.annotation.XmlRes;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
@@ -96,6 +97,7 @@
 import dalvik.system.VMRuntime;
 
 import java.io.File;
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.security.cert.Certificate;
@@ -108,6 +110,7 @@
 import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * Class for retrieving various kinds of information related to the application
@@ -11426,4 +11429,60 @@
         throw new UnsupportedOperationException(
                 "unregisterPackageMonitorCallback not implemented in subclass");
     }
+
+    /**
+     * Retrieve AndroidManifest.xml information for the given application apk path.
+     *
+     * <p>Example:
+     *
+     * <pre><code>
+     * Bundle result;
+     * try {
+     *     result = getContext().getPackageManager().parseAndroidManifest(apkFilePath,
+     *             xmlResourceParser -> {
+     *                 Bundle bundle = new Bundle();
+     *                 // Search the start tag
+     *                 int type;
+     *                 while ((type = xmlResourceParser.next()) != XmlPullParser.START_TAG
+     *                         &amp;&amp; type != XmlPullParser.END_DOCUMENT) {
+     *                 }
+     *                 if (type != XmlPullParser.START_TAG) {
+     *                     return bundle;
+     *                 }
+     *
+     *                 // Start to read the tags and attributes from the xmlResourceParser
+     *                 if (!xmlResourceParser.getName().equals("manifest")) {
+     *                     return bundle;
+     *                 }
+     *                 String packageName = xmlResourceParser.getAttributeValue(null, "package");
+     *                 bundle.putString("package", packageName);
+     *
+     *                 // Continue to read the tags and attributes from the xmlResourceParser
+     *
+     *                 return bundle;
+     *             });
+     * } catch (IOException e) {
+     * }
+     * </code></pre>
+     *
+     * Note: When the parserFunction is invoked, the client can read the AndroidManifest.xml
+     * information by the XmlResourceParser object. After leaving the parserFunction, the
+     * XmlResourceParser object will be closed.
+     *
+     * @param apkFilePath The path of an application apk file.
+     * @param parserFunction The parserFunction will be invoked with the XmlResourceParser object
+     *        after getting the AndroidManifest.xml of an application package.
+     *
+     * @return Returns the result of the {@link Function#apply(Object)}.
+     *
+     * @throws IOException if the AndroidManifest.xml of an application package cannot be
+     *             read or accessed.
+     */
+    @FlaggedApi(android.content.pm.Flags.FLAG_GET_PACKAGE_INFO)
+    @WorkerThread
+    public <T> T parseAndroidManifest(@NonNull String apkFilePath,
+            @NonNull Function<XmlResourceParser, T> parserFunction) throws IOException {
+        throw new UnsupportedOperationException(
+                "parseAndroidManifest not implemented in subclass");
+    }
 }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 89f889f..fe95a2a 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1414,7 +1414,7 @@
      * otherwise the value will be equal to 1.
      * Note that this level is just a number of supported levels (the granularity of control).
      * There is no actual physical power units tied to this level.</p>
-     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * <p>This key is available on all devices.</p>
      *
      * @see CaptureRequest#FLASH_MODE
      */
@@ -1430,7 +1430,7 @@
      * or equal to <code>android.flash.info.singleStrengthMaxLevel</code>.
      * Note for devices that do not support the manual flash strength control
      * feature, this level will always be equal to 1.</p>
-     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * <p>This key is available on all devices.</p>
      */
     @PublicKey
     @NonNull
@@ -1450,7 +1450,7 @@
      * android.flash.info.singleStrengthMaxLevel i.e. the ratio of
      * android.flash.info.torchStrengthMaxLevel:android.flash.info.singleStrengthMaxLevel
      * is not guaranteed to be the ratio of actual brightness.</p>
-     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * <p>This key is available on all devices.</p>
      *
      * @see CaptureRequest#FLASH_MODE
      */
@@ -1466,7 +1466,7 @@
      * or equal to android.flash.info.torchStrengthMaxLevel.
      * Note for the devices that do not support the manual flash strength control feature,
      * this level will always be equal to 1.</p>
-     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * <p>This key is available on all devices.</p>
      */
     @PublicKey
     @NonNull
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 5d06978..93cae54 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2688,7 +2688,7 @@
      * set to TORCH;
      * <code>[1-android.flash.info.singleStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
      * set to SINGLE</p>
-     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * <p>This key is available on all devices.</p>
      *
      * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#FLASH_MODE
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 0d204f3..12ab0f6 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2974,7 +2974,7 @@
      * set to TORCH;
      * <code>[1-android.flash.info.singleStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
      * set to SINGLE</p>
-     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * <p>This key is available on all devices.</p>
      *
      * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#FLASH_MODE
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index 7756b9c..c379188 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -22,9 +22,11 @@
 import android.content.Context;
 import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
 import android.os.Binder;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.Trace;
 import android.util.ArrayMap;
 
 import com.android.internal.annotations.GuardedBy;
@@ -45,6 +47,8 @@
 @VisibleForTesting(visibility = Visibility.PACKAGE)
 public final class DeviceStateManagerGlobal {
     private static DeviceStateManagerGlobal sInstance;
+    private static final String TAG = "DeviceStateManagerGlobal";
+    private static final boolean DEBUG = Build.IS_DEBUGGABLE;
 
     /**
      * Returns an instance of {@link DeviceStateManagerGlobal}. May return {@code null} if a
@@ -400,11 +404,29 @@
         }
 
         void notifyBaseStateChanged(int newBaseState) {
-            mExecutor.execute(() -> mDeviceStateCallback.onBaseStateChanged(newBaseState));
+            execute("notifyBaseStateChanged",
+                    () -> mDeviceStateCallback.onBaseStateChanged(newBaseState));
         }
 
         void notifyStateChanged(int newDeviceState) {
-            mExecutor.execute(() -> mDeviceStateCallback.onStateChanged(newDeviceState));
+            execute("notifyStateChanged",
+                    () -> mDeviceStateCallback.onStateChanged(newDeviceState));
+        }
+
+        private void execute(String traceName, Runnable r) {
+            mExecutor.execute(() -> {
+                if (DEBUG) {
+                    Trace.beginSection(
+                            mDeviceStateCallback.getClass().getSimpleName() + "#" + traceName);
+                }
+                try {
+                    r.run();
+                } finally {
+                    if (DEBUG) {
+                        Trace.endSection();
+                    }
+                }
+            });
         }
     }
 
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 4791a83..f71e853 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -721,8 +721,19 @@
     public interface DisplayOffloadSession {
         /** Provide the display state to use in place of state DOZE. */
         void setDozeStateOverride(int displayState);
-        /** Returns the associated DisplayOffloader. */
-        DisplayOffloader getDisplayOffloader();
+
+        /** Whether the session is active. */
+        boolean isActive();
+
+        /**
+         * Update the brightness from the offload chip.
+         * @param brightness The brightness value between {@link PowerManager.BRIGHTNESS_MIN} and
+         *                   {@link PowerManager.BRIGHTNESS_MAX}, or
+         *                   {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} which removes
+         *                   the brightness from offload. Other values will be ignored.
+         */
+        void updateBrightness(float brightness);
+
         /** Returns whether displayoffload supports the given display state. */
         static boolean isSupportedOffloadState(int displayState) {
             return Display.isSuspendedState(displayState);
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index f817fb8..1100731 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -82,8 +82,8 @@
         private static final int MAIN_INDEX_SIZE = 1 <<  LOG_MAIN_INDEX_SIZE;
         private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
         /**
-         * Debuggable builds will throw an BinderProxyMapSizeException if the number of
-         * map entries exceeds:
+         * We will throw a BinderProxyMapSizeException if the number of map entries
+         * exceeds:
          */
         private static final int CRASH_AT_SIZE = 25_000;
 
diff --git a/core/java/android/permission/IOnPermissionsChangeListener.aidl b/core/java/android/permission/IOnPermissionsChangeListener.aidl
index afacf1a..c68c0c9 100644
--- a/core/java/android/permission/IOnPermissionsChangeListener.aidl
+++ b/core/java/android/permission/IOnPermissionsChangeListener.aidl
@@ -21,5 +21,5 @@
  * {@hide}
  */
 oneway interface IOnPermissionsChangeListener {
-    void onPermissionsChanged(int uid, String deviceId);
+    void onPermissionsChanged(int uid, String persistentDeviceId);
 }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 7a158c5..91adc37 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1738,8 +1738,9 @@
         }
 
         @Override
-        public void onPermissionsChanged(int uid, String deviceId) {
-            mHandler.obtainMessage(MSG_PERMISSIONS_CHANGED, uid, 0, deviceId).sendToTarget();
+        public void onPermissionsChanged(int uid, String persistentDeviceId) {
+            mHandler.obtainMessage(MSG_PERMISSIONS_CHANGED, uid, 0, persistentDeviceId)
+                    .sendToTarget();
         }
 
         @Override
@@ -1747,8 +1748,8 @@
             switch (msg.what) {
                 case MSG_PERMISSIONS_CHANGED: {
                     final int uid = msg.arg1;
-                    final String deviceId = msg.obj.toString();
-                    mListener.onPermissionsChanged(uid, deviceId);
+                    final String persistentDeviceId = msg.obj.toString();
+                    mListener.onPermissionsChanged(uid, persistentDeviceId);
                     return true;
                 }
                 default:
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 33c15d77..ff6ec29 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -36,6 +37,7 @@
 import android.app.AppOpsManager;
 import android.app.Application;
 import android.app.AutomaticZenRule;
+import android.app.Flags;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.SearchManager;
@@ -1904,6 +1906,36 @@
             = "android.settings.ACTION_CONDITION_PROVIDER_SETTINGS";
 
     /**
+     * Activity Action: Shows the settings page for an {@link AutomaticZenRule} mode.
+     * <p>
+     * Users can change the behavior of the mode when it's activated and access the owning app's
+     * additional configuration screen, where triggering criteria can be modified (see
+     * {@link AutomaticZenRule#setConfigurationActivity(ComponentName)}).
+     * <p>
+     * A matching Activity will only be found if
+     * {@link NotificationManager#areAutomaticZenRulesUserManaged()} is true.
+     * <p>
+     * Input: Intent's data URI set with an application name, using the "package" schema (like
+     * "package:com.my.app").
+     * Input: The id of the rule, provided in {@link #EXTRA_AUTOMATIC_ZEN_RULE_ID}.
+     * <p>
+     * Output: Nothing.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_AUTOMATIC_ZEN_RULE_SETTINGS
+            = "android.settings.AUTOMATIC_ZEN_RULE_SETTINGS";
+
+    /**
+     * Activity Extra: The String id of the {@link AutomaticZenRule mode} settings to display.
+     * <p>
+     * This must be passed as an extra field to the {@link #ACTION_AUTOMATIC_ZEN_RULE_SETTINGS}.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final String EXTRA_AUTOMATIC_ZEN_RULE_ID
+            = "android.provider.extra.AUTOMATIC_ZEN_RULE_ID";
+
+    /**
      * Activity Action: Show settings for video captioning.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you safeguard
diff --git a/core/java/android/service/notification/OWNERS b/core/java/android/service/notification/OWNERS
index bb0e6ab..cb0b5fa 100644
--- a/core/java/android/service/notification/OWNERS
+++ b/core/java/android/service/notification/OWNERS
@@ -2,6 +2,7 @@
 
 juliacr@google.com
 yurilin@google.com
+matiashe@google.com
 jeffdq@google.com
 dsandler@android.com
 dsandler@google.com
diff --git a/core/java/android/service/timezone/TEST_MAPPING b/core/java/android/service/timezone/TEST_MAPPING
index 21a8eab..e5910ea 100644
--- a/core/java/android/service/timezone/TEST_MAPPING
+++ b/core/java/android/service/timezone/TEST_MAPPING
@@ -1,6 +1,5 @@
 {
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FrameworksTimeCoreTests",
       "options": [
@@ -8,7 +7,10 @@
           "include-filter": "android.service."
         }
       ]
-    },
+    }
+  ],
+  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+  "postsubmit": [
     {
       "name": "CtsLocationTimeZoneManagerHostTest"
     }
diff --git a/core/java/android/util/DataUnit.java b/core/java/android/util/DataUnit.java
index cc33af3..10905e1 100644
--- a/core/java/android/util/DataUnit.java
+++ b/core/java/android/util/DataUnit.java
@@ -32,6 +32,7 @@
  *
  * @hide
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public enum DataUnit {
     KILOBYTES { @Override public long toBytes(long v) { return v * 1_000; } },
     MEGABYTES { @Override public long toBytes(long v) { return v * 1_000_000; } },
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 4654dbf..d2c5975 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -48,6 +48,9 @@
  * They carry a payload of one or more int, long, or String values.  The
  * event-log-tags file defines the payload contents for each type code.
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
+        "com.android.hoststubgen.nativesubstitution.EventLog_host")
 public class EventLog {
     /** @hide */ public EventLog() {}
 
@@ -416,6 +419,7 @@
     /**
      * Read TAGS_FILE, populating sTagCodes and sTagNames, if not already done.
      */
+    @android.ravenwood.annotation.RavenwoodReplace
     private static synchronized void readTagsFile() {
         if (sTagCodes != null && sTagNames != null) return;
 
@@ -441,8 +445,7 @@
                 try {
                     int num = Integer.parseInt(m.group(1));
                     String name = m.group(2);
-                    sTagCodes.put(name, num);
-                    sTagNames.put(num, name);
+                    registerTagLocked(num, name);
                 } catch (NumberFormatException e) {
                     Log.wtf(TAG, "Error in " + TAGS_FILE + ": " + line, e);
                 }
@@ -454,4 +457,20 @@
             try { if (reader != null) reader.close(); } catch (IOException e) {}
         }
     }
+
+    private static void registerTagLocked(int num, String name) {
+        sTagCodes.put(name, num);
+        sTagNames.put(num, name);
+    }
+
+    private static synchronized void readTagsFile$ravenwood() {
+        // TODO: restore parsing logic once we carry into runtime
+        sTagCodes = new HashMap<String, Integer>();
+        sTagNames = new HashMap<Integer, String>();
+
+        // Hard-code a few common tags
+        registerTagLocked(524288, "sysui_action");
+        registerTagLocked(524290, "sysui_count");
+        registerTagLocked(524291, "sysui_histogram");
+    }
 }
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index c04a71c..413ae1f 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -26,6 +26,7 @@
  *
  * @hide
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public class IntArray implements Cloneable {
     private static final int MIN_CAPACITY_INCREMENT = 12;
 
diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java
index 3101c0d..4c7ef08 100644
--- a/core/java/android/util/LongArray.java
+++ b/core/java/android/util/LongArray.java
@@ -30,6 +30,7 @@
  *
  * @hide
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public class LongArray implements Cloneable {
     private static final int MIN_CAPACITY_INCREMENT = 12;
 
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index 3aeecca..c0ceb9ea 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -31,6 +31,7 @@
  * @hide
  */
 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public final class Slog {
 
     private Slog() {
@@ -216,6 +217,7 @@
      * @see Log#wtf(String, String)
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @android.ravenwood.annotation.RavenwoodThrow
     public static int wtf(@Nullable String tag, @NonNull String msg) {
         return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null, false, true);
     }
@@ -223,6 +225,7 @@
     /**
      * Similar to {@link #wtf(String, String)}, but does not output anything to the log.
      */
+    @android.ravenwood.annotation.RavenwoodThrow
     public static void wtfQuiet(@Nullable String tag, @NonNull String msg) {
         Log.wtfQuiet(Log.LOG_ID_SYSTEM, tag, msg, true);
     }
@@ -241,6 +244,7 @@
      * @see Log#wtfStack(String, String)
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @android.ravenwood.annotation.RavenwoodThrow
     public static int wtfStack(@Nullable String tag, @NonNull String msg) {
         return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null, true, true);
     }
@@ -259,6 +263,7 @@
      *
      * @see Log#wtf(String, Throwable)
      */
+    @android.ravenwood.annotation.RavenwoodThrow
     public static int wtf(@Nullable String tag, @Nullable Throwable tr) {
         return Log.wtf(Log.LOG_ID_SYSTEM, tag, tr.getMessage(), tr, false, true);
     }
@@ -279,6 +284,7 @@
      * @see Log#wtf(String, String, Throwable)
      */
     @UnsupportedAppUsage
+    @android.ravenwood.annotation.RavenwoodThrow
     public static int wtf(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) {
         return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, tr, false, true);
     }
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index d06b0ce..bff8db1 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -41,6 +41,8 @@
 /**
  * A class containing utility methods related to time zones.
  */
+@android.ravenwood.annotation.RavenwoodKeepPartialClass
+@android.ravenwood.annotation.RavenwoodKeepStaticInitializer
 public class TimeUtils {
     /** @hide */ public TimeUtils() {}
     /** {@hide} */
@@ -180,6 +182,7 @@
     private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10];
     private static char[] sTmpFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10];
 
+    @android.ravenwood.annotation.RavenwoodKeep
     static private int accumField(int amt, int suffix, boolean always, int zeropad) {
         if (amt > 999) {
             int num = 0;
@@ -202,6 +205,7 @@
         return 0;
     }
 
+    @android.ravenwood.annotation.RavenwoodKeep
     static private int printFieldLocked(char[] formatStr, int amt, char suffix, int pos,
             boolean always, int zeropad) {
         if (always || amt > 0) {
@@ -242,6 +246,7 @@
         return pos;
     }
 
+    @android.ravenwood.annotation.RavenwoodKeep
     private static int formatDurationLocked(long duration, int fieldLen) {
         if (sFormatStr.length < fieldLen) {
             sFormatStr = new char[fieldLen];
@@ -314,6 +319,7 @@
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @android.ravenwood.annotation.RavenwoodKeep
     public static void formatDuration(long duration, StringBuilder builder) {
         synchronized (sFormatSync) {
             int len = formatDurationLocked(duration, 0);
@@ -322,6 +328,7 @@
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @android.ravenwood.annotation.RavenwoodKeep
     public static void formatDuration(long duration, StringBuilder builder, int fieldLen) {
         synchronized (sFormatSync) {
             int len = formatDurationLocked(duration, fieldLen);
@@ -331,6 +338,7 @@
 
     /** @hide Just for debugging; not internationalized. */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @android.ravenwood.annotation.RavenwoodKeep
     public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {
         synchronized (sFormatSync) {
             int len = formatDurationLocked(duration, fieldLen);
@@ -340,6 +348,7 @@
 
     /** @hide Just for debugging; not internationalized. */
     @TestApi
+    @android.ravenwood.annotation.RavenwoodKeep
     public static String formatDuration(long duration) {
         synchronized (sFormatSync) {
             int len = formatDurationLocked(duration, 0);
@@ -349,11 +358,13 @@
 
     /** @hide Just for debugging; not internationalized. */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @android.ravenwood.annotation.RavenwoodKeep
     public static void formatDuration(long duration, PrintWriter pw) {
         formatDuration(duration, pw, 0);
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @android.ravenwood.annotation.RavenwoodKeep
     public static void formatDuration(long time, long now, StringBuilder sb) {
         if (time == 0) {
             sb.append("--");
@@ -363,6 +374,7 @@
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @android.ravenwood.annotation.RavenwoodKeep
     public static void formatDuration(long time, long now, PrintWriter pw) {
         if (time == 0) {
             pw.print("--");
@@ -372,16 +384,19 @@
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @android.ravenwood.annotation.RavenwoodKeep
     public static String formatUptime(long time) {
         return formatTime(time, SystemClock.uptimeMillis());
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @android.ravenwood.annotation.RavenwoodKeep
     public static String formatRealtime(long time) {
         return formatTime(time, SystemClock.elapsedRealtime());
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @android.ravenwood.annotation.RavenwoodKeep
     public static String formatTime(long time, long referenceTime) {
         long diff = time - referenceTime;
         if (diff > 0) {
@@ -402,6 +417,7 @@
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @android.ravenwood.annotation.RavenwoodKeep
     public static String logTimeOfDay(long millis) {
         Calendar c = Calendar.getInstance();
         if (millis >= 0) {
@@ -413,6 +429,7 @@
     }
 
     /** {@hide} */
+    @android.ravenwood.annotation.RavenwoodKeep
     public static String formatForLogging(long millis) {
         if (millis <= 0) {
             return "unknown";
@@ -426,6 +443,7 @@
      *
      * @hide
      */
+    @android.ravenwood.annotation.RavenwoodKeep
     public static void dumpTime(PrintWriter pw, long time) {
         pw.print(sDumpDateFormat.format(new Date(time)));
     }
@@ -457,6 +475,7 @@
      *
      * @hide
      */
+    @android.ravenwood.annotation.RavenwoodKeep
     public static void dumpTimeWithDelta(PrintWriter pw, long time, long now) {
         pw.print(sDumpDateFormat.format(new Date(time)));
         if (time == now) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 43bfe13..53aed49 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -23,7 +23,7 @@
 
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
-import android.annotation.Hide;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -752,6 +752,7 @@
      *     {@link #isGranularScrollingSupported()} to check if granular scrolling is supported.
      * </p>
      */
+    @FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING)
     public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT =
             "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
 
@@ -2608,6 +2609,7 @@
      * @return True if all scroll actions that could support
      * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so, false otherwise.
      */
+    @FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING)
     public boolean isGranularScrollingSupported() {
         return getBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING);
     }
@@ -2626,6 +2628,7 @@
      *
      * @throws IllegalStateException If called from an AccessibilityService.
      */
+    @FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING)
     public void setGranularScrollingSupported(boolean granularScrollingSupported) {
         setBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING,
                 granularScrollingSupported);
@@ -6119,6 +6122,7 @@
          * This should be used for {@code mItemCount} and
          * {@code mImportantForAccessibilityItemCount} when values for those fields are not known.
          */
+        @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
         public static final int UNDEFINED = -1;
 
         private int mRowCount;
@@ -6229,8 +6233,8 @@
          *                  the item count is not known.
          * @param importantForAccessibilityItemCount The count of the collection's views considered
          *                                           important for accessibility.
+         * @hide
          */
-        @Hide
         public CollectionInfo(int rowCount, int columnCount, boolean hierarchical,
                 int selectionMode, int itemCount, int importantForAccessibilityItemCount) {
             mRowCount = rowCount;
@@ -6287,6 +6291,7 @@
          *
          * @return The count of items, which may be {@code UNDEFINED} if the count is not known.
          */
+        @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
         public int getItemCount() {
             return mItemCount;
         }
@@ -6297,6 +6302,7 @@
          * @return The count of items important for accessibility, which may be {@code UNDEFINED}
          * if the count is not known.
          */
+        @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
         public int getImportantForAccessibilityItemCount() {
             return mImportantForAccessibilityItemCount;
         }
@@ -6323,6 +6329,7 @@
          * The builder for CollectionInfo.
          */
 
+        @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
         public static final class Builder {
             private int mRowCount = 0;
             private int mColumnCount = 0;
@@ -6334,6 +6341,7 @@
             /**
              * Creates a new Builder.
              */
+            @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
             public Builder() {
             }
 
@@ -6343,6 +6351,7 @@
              * @return This builder.
              */
             @NonNull
+            @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
             public CollectionInfo.Builder setRowCount(int rowCount) {
                 mRowCount = rowCount;
                 return this;
@@ -6354,6 +6363,7 @@
              * @return This builder.
              */
             @NonNull
+            @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
             public CollectionInfo.Builder setColumnCount(int columnCount) {
                 mColumnCount = columnCount;
                 return this;
@@ -6364,6 +6374,7 @@
              * @return This builder.
              */
             @NonNull
+            @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
             public CollectionInfo.Builder setHierarchical(boolean hierarchical) {
                 mHierarchical = hierarchical;
                 return this;
@@ -6375,6 +6386,7 @@
              * @return This builder.
              */
             @NonNull
+            @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
             public CollectionInfo.Builder setSelectionMode(int selectionMode) {
                 mSelectionMode = selectionMode;
                 return this;
@@ -6389,6 +6401,7 @@
              * @return This builder.
              */
             @NonNull
+            @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
             public CollectionInfo.Builder setItemCount(int itemCount) {
                 mItemCount = itemCount;
                 return this;
@@ -6401,6 +6414,7 @@
              * @return This builder.
              */
             @NonNull
+            @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
             public CollectionInfo.Builder setImportantForAccessibilityItemCount(
                     int importantForAccessibilityItemCount) {
                 mImportantForAccessibilityItemCount = importantForAccessibilityItemCount;
@@ -6411,6 +6425,7 @@
              * Creates a new {@link CollectionInfo} instance.
              */
             @NonNull
+            @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
             public CollectionInfo build() {
                 CollectionInfo collectionInfo = new CollectionInfo(mRowCount, mColumnCount,
                         mHierarchical);
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 950fa4b..c337cb4 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -17,6 +17,13 @@
 }
 
 flag {
+    namespace: "accessibility"
+    name: "collection_info_item_counts"
+    description: "Fields for total items and the number of important for accessibility items in a collection"
+    bug: "302376158"
+}
+
+flag {
     name: "deduplicate_accessibility_warning_dialog"
     namespace: "accessibility"
     description: "Removes duplicate definition of the accessibility warning dialog."
@@ -39,6 +46,13 @@
 
 flag {
     namespace: "accessibility"
+    name: "granular_scrolling"
+    description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen"
+    bug: "302376158"
+}
+
+flag {
+    namespace: "accessibility"
     name: "update_always_on_a11y_service"
     description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut."
     bug: "298869916"
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 42a1f1e..0da03fb 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -37,5 +37,14 @@
     namespace: "window_surfaces"
     name: "remove_capture_display"
     description: "Remove uses of ScreenCapture#captureDisplay"
+    is_fixed_read_only: true
     bug: "293445881"
-}
\ No newline at end of file
+}
+
+flag {
+    namespace: "window_surfaces"
+    name: "allow_disable_activity_record_input_sink"
+    description: "Whether to allow system activity to disable ActivityRecordInputSink"
+    is_fixed_read_only: true
+    bug: "262477923"
+}
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index b600b22..933cc49 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -9,6 +9,16 @@
     bug: "260873529"
 }
 
+# Using a fixed read only flag because there are ClientTransaction scheduling before
+# WindowManagerService creation.
+flag {
+    namespace: "windowing_sdk"
+    name: "bundle_client_transaction_flag"
+    description: "To bundle multiple ClientTransactionItems into one ClientTransaction"
+    bug: "260873529"
+    is_fixed_read_only: true
+}
+
 flag {
     namespace: "windowing_sdk"
     name: "activity_embedding_overlay_presentation_flag"
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index df6c153..7be27be 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -54,22 +54,6 @@
      */
     public static final class NotificationFlags {
 
-        /**
-         * FOR DEVELOPMENT / TESTING ONLY!!!
-         * Forcibly demote *ALL* FSI notifications as if no apps have the app op permission.
-         * NOTE: enabling this implies SHOW_STICKY_HUN_FOR_DENIED_FSI in SystemUI
-         */
-        public static final Flag FSI_FORCE_DEMOTE =
-                devFlag("persist.sysui.notification.fsi_force_demote");
-
-        /** Gating the feature which shows FSI-denied notifications as Sticky HUNs */
-        public static final Flag SHOW_STICKY_HUN_FOR_DENIED_FSI =
-                releasedFlag("persist.sysui.notification.show_sticky_hun_for_denied_fsi");
-
-        /** Gating the redaction of OTP notifications on the lockscreen */
-        public static final Flag OTP_REDACTION =
-                devFlag("persist.sysui.notification.otp_redaction");
-
         /** Gating the logging of DND state change events. */
         public static final Flag LOG_DND_STATE_EVENTS =
                 releasedFlag("persist.sysui.notification.log_dnd_state_events");
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 5c1d91f..5b68e8e 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -184,11 +184,11 @@
 void NativeInputEventReceiver::setFdEvents(int events) {
     if (mFdEvents != events) {
         mFdEvents = events;
-        int fd = mInputConsumer.getChannel()->getFd();
+        auto&& fd = mInputConsumer.getChannel()->getFd();
         if (events) {
-            mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
+            mMessageQueue->getLooper()->addFd(fd.get(), 0, events, this, nullptr);
         } else {
-            mMessageQueue->getLooper()->removeFd(fd);
+            mMessageQueue->getLooper()->removeFd(fd.get());
         }
     }
 }
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 833952d..6bdf821 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -102,8 +102,8 @@
 }
 
 status_t NativeInputEventSender::initialize() {
-    int receiveFd = mInputPublisher.getChannel()->getFd();
-    mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
+    auto&& receiveFd = mInputPublisher.getChannel()->getFd();
+    mMessageQueue->getLooper()->addFd(receiveFd.get(), 0, ALOOPER_EVENT_INPUT, this, NULL);
     return OK;
 }
 
@@ -112,7 +112,7 @@
         LOG(DEBUG) << "channel '" << getInputChannelName() << "' ~ Disposing input event sender.";
     }
 
-    mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd());
+    mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd().get());
 }
 
 status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7b075e6..4d208c6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2301,6 +2301,7 @@
     <!-- Allows system apps to call methods to register itself as a mDNS offload engine.
         <p>Not for use by third-party or privileged applications.
         @SystemApi
+        @FlaggedApi("com.android.net.flags.register_nsd_offload_engine")
         @hide This should only be used by system apps.
     -->
     <permission android:name="android.permission.REGISTER_NSD_OFFLOAD_ENGINE"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 68bad45..6cd6eb4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5465,6 +5465,7 @@
         <item>1,1,1.0,.15,15</item>
         <item>0,0,0.7,0,1</item>
         <item>0,0,0.83333,0,1</item>
+        <item>0,0,1.1667,0,1</item>
     </string-array>
 
     <!-- The integer index of the selected option in config_udfps_touch_detection_options -->
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 4b02257..2327b20 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -42,6 +42,7 @@
 import android.app.PictureInPictureParams;
 import android.app.ResourcesManager;
 import android.app.servertransaction.ActivityConfigurationChangeItem;
+import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
@@ -73,6 +74,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.content.ReferrerIntent;
+import com.android.window.flags.Flags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -227,7 +229,8 @@
         try {
             // Send process level config change.
             ClientTransaction transaction = newTransaction(activityThread);
-            transaction.addCallback(ConfigurationChangeItem.obtain(newConfig, DEVICE_ID_INVALID));
+            addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+                    newConfig, DEVICE_ID_INVALID));
             appThread.scheduleTransaction(transaction);
             InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
@@ -243,7 +246,7 @@
             newConfig.seq++;
             newConfig.smallestScreenWidthDp++;
             transaction = newTransaction(activityThread);
-            transaction.addCallback(ActivityConfigurationChangeItem.obtain(
+            addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
                     activity.getActivityToken(), newConfig));
             appThread.scheduleTransaction(transaction);
             InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -444,16 +447,16 @@
         activity.mTestLatch = new CountDownLatch(1);
 
         ClientTransaction transaction = newTransaction(activityThread);
-        transaction.addCallback(ConfigurationChangeItem.obtain(
+        addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
                 processConfigLandscape, DEVICE_ID_INVALID));
         appThread.scheduleTransaction(transaction);
 
         transaction = newTransaction(activityThread);
-        transaction.addCallback(ActivityConfigurationChangeItem.obtain(
+        addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
                 activity.getActivityToken(), activityConfigLandscape));
-        transaction.addCallback(ConfigurationChangeItem.obtain(
+        addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
                 processConfigPortrait, DEVICE_ID_INVALID));
-        transaction.addCallback(ActivityConfigurationChangeItem.obtain(
+        addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
                 activity.getActivityToken(), activityConfigPortrait));
         appThread.scheduleTransaction(transaction);
 
@@ -840,8 +843,8 @@
                         false /* shouldSendCompatFakeFocus*/);
 
         final ClientTransaction transaction = newTransaction(activity);
-        transaction.addCallback(callbackItem);
-        transaction.setLifecycleStateRequest(resumeStateRequest);
+        addClientTransactionItem(transaction, callbackItem);
+        addClientTransactionItem(transaction, resumeStateRequest);
 
         return transaction;
     }
@@ -853,7 +856,7 @@
                         false /* shouldSendCompatFakeFocus */);
 
         final ClientTransaction transaction = newTransaction(activity);
-        transaction.setLifecycleStateRequest(resumeStateRequest);
+        addClientTransactionItem(transaction, resumeStateRequest);
 
         return transaction;
     }
@@ -864,7 +867,7 @@
                 activity.getActivityToken(), 0 /* configChanges */);
 
         final ClientTransaction transaction = newTransaction(activity);
-        transaction.setLifecycleStateRequest(stopStateRequest);
+        addClientTransactionItem(transaction, stopStateRequest);
 
         return transaction;
     }
@@ -876,7 +879,7 @@
                 activity.getActivityToken(), config);
 
         final ClientTransaction transaction = newTransaction(activity);
-        transaction.addCallback(item);
+        addClientTransactionItem(transaction, item);
 
         return transaction;
     }
@@ -888,7 +891,7 @@
                 resume);
 
         final ClientTransaction transaction = newTransaction(activity);
-        transaction.addCallback(item);
+        addClientTransactionItem(transaction, item);
 
         return transaction;
     }
@@ -903,6 +906,17 @@
         return ClientTransaction.obtain(activityThread.getApplicationThread());
     }
 
+    private static void addClientTransactionItem(@NonNull ClientTransaction transaction,
+            @NonNull ClientTransactionItem item) {
+        if (Flags.bundleClientTransactionFlag()) {
+            transaction.addTransactionItem(item);
+        } else if (item.isActivityLifecycleItem()) {
+            transaction.setLifecycleStateRequest((ActivityLifecycleItem) item);
+        } else {
+            transaction.addCallback(item);
+        }
+    }
+
     // Test activity
     public static class TestActivity extends Activity {
         static final String PIP_REQUESTED_OVERRIDE_ENTER = "pip_requested_override_enter";
diff --git a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
index 2ec58d4..5a202c5 100644
--- a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
+++ b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -43,11 +44,29 @@
 @LargeTest
 public class ParcelableUsageEventListTest {
     private static final int SMALL_TEST_EVENT_COUNT = 100;
-    private static final int LARGE_TEST_EVENT_COUNT = 10000;
+    private static final int LARGE_TEST_EVENT_COUNT = 30000;
 
     private Random mRandom = new Random();
 
     @Test
+    public void testNullList() throws Exception {
+        Parcel parcel = Parcel.obtain();
+        try {
+            parcel.writeParcelable(new ParcelableUsageEventList(null), 0);
+            fail("Expected IllegalArgumentException with null list.");
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    @Test
+    public void testEmptyList() throws Exception {
+        testParcelableUsageEventList(0);
+    }
+
+    @Test
     public void testSmallList() throws Exception {
         testParcelableUsageEventList(SMALL_TEST_EVENT_COUNT);
     }
@@ -58,15 +77,15 @@
     }
 
     private void testParcelableUsageEventList(int eventCount) throws Exception {
-        List<Event> smallList = new ArrayList<>();
+        List<Event> eventList = new ArrayList<>();
         for (int i = 0; i < eventCount; i++) {
-            smallList.add(generateUsageEvent());
+            eventList.add(generateUsageEvent());
         }
 
         ParcelableUsageEventList slice;
         Parcel parcel = Parcel.obtain();
         try {
-            parcel.writeParcelable(new ParcelableUsageEventList(smallList), 0);
+            parcel.writeParcelable(new ParcelableUsageEventList(eventList), 0);
             parcel.setDataPosition(0);
             slice = parcel.readParcelable(getClass().getClassLoader(),
                     ParcelableUsageEventList.class);
@@ -79,7 +98,7 @@
         assertEquals(eventCount, slice.getList().size());
 
         for (int i = 0; i < eventCount; i++) {
-            compareUsageEvent(smallList.get(i), slice.getList().get(i));
+            compareUsageEvent(eventList.get(i), slice.getList().get(i));
         }
     }
 
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index 580e73c..06340a2 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -35,6 +35,7 @@
         "androidx.test.ext.junit",
         "truth",
         "servicestests-utils",
+        "ravenwood-junit",
     ],
 
     libs: [
@@ -50,3 +51,22 @@
     test_suites: ["device-tests"],
 
 }
+
+android_ravenwood_test {
+    name: "FrameworksUtilTestsRavenwood",
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.test.rules",
+    ],
+    srcs: [
+        "src/android/util/DataUnitTest.java",
+        "src/android/util/EventLogTest.java",
+        "src/android/util/IndentingPrintWriterTest.java",
+        "src/android/util/IntArrayTest.java",
+        "src/android/util/LocalLogTest.java",
+        "src/android/util/LongArrayTest.java",
+        "src/android/util/SlogTest.java",
+        "src/android/util/TimeUtilsTest.java",
+    ],
+    auto_gen_config: true,
+}
diff --git a/core/tests/coretests/src/android/util/DataUnitTest.java b/core/tests/utiltests/src/android/util/DataUnitTest.java
similarity index 85%
rename from core/tests/coretests/src/android/util/DataUnitTest.java
rename to core/tests/utiltests/src/android/util/DataUnitTest.java
index 034cbdd..af9ebc8 100644
--- a/core/tests/coretests/src/android/util/DataUnitTest.java
+++ b/core/tests/utiltests/src/android/util/DataUnitTest.java
@@ -16,12 +16,18 @@
 
 package android.util;
 
-import androidx.test.filters.SmallTest;
+import static org.junit.Assert.assertEquals;
 
-import junit.framework.TestCase;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @SmallTest
-public class DataUnitTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class DataUnitTest {
+    @Test
     public void testSi() throws Exception {
         assertEquals(12_000L, DataUnit.KILOBYTES.toBytes(12));
         assertEquals(12_000_000L, DataUnit.MEGABYTES.toBytes(12));
@@ -29,6 +35,7 @@
         assertEquals(12_000_000_000_000L, DataUnit.TERABYTES.toBytes(12));
     }
 
+    @Test
     public void testIec() throws Exception {
         assertEquals(12_288L, DataUnit.KIBIBYTES.toBytes(12));
         assertEquals(12_582_912L, DataUnit.MEBIBYTES.toBytes(12));
diff --git a/core/tests/coretests/src/android/util/EventLogTest.java b/core/tests/utiltests/src/android/util/EventLogTest.java
similarity index 78%
rename from core/tests/coretests/src/android/util/EventLogTest.java
rename to core/tests/utiltests/src/android/util/EventLogTest.java
index 94e72c4..0ebf2c1 100644
--- a/core/tests/coretests/src/android/util/EventLogTest.java
+++ b/core/tests/utiltests/src/android/util/EventLogTest.java
@@ -16,23 +16,42 @@
 
 package android.util;
 
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.EventLog.Event;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import junit.framework.AssertionFailedError;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /** Unit tests for {@link android.util.EventLog} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class EventLogTest {
+    @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule();
 
     @Test
+    public void testSimple() throws Throwable {
+        EventLog.writeEvent(42, 42);
+        EventLog.writeEvent(42, 42L);
+        EventLog.writeEvent(42, 42f);
+        EventLog.writeEvent(42, "forty-two");
+        EventLog.writeEvent(42, 42, "forty-two", null, 42);
+    }
+
+    @Test
+    @IgnoreUnderRavenwood(reason = "Reading not yet supported")
     public void testWithNewData() throws Throwable {
         Event event = createEvent(() -> {
             EventLog.writeEvent(314,  123);
diff --git a/core/tests/coretests/src/android/util/LocalLogTest.java b/core/tests/utiltests/src/android/util/LocalLogTest.java
similarity index 93%
rename from core/tests/coretests/src/android/util/LocalLogTest.java
rename to core/tests/utiltests/src/android/util/LocalLogTest.java
index d4861cd..5025a3d 100644
--- a/core/tests/coretests/src/android/util/LocalLogTest.java
+++ b/core/tests/utiltests/src/android/util/LocalLogTest.java
@@ -16,9 +16,13 @@
 
 package android.util;
 
-import androidx.test.filters.LargeTest;
+import static org.junit.Assert.assertTrue;
 
-import junit.framework.TestCase;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -27,13 +31,16 @@
 import java.util.List;
 
 @LargeTest
-public class LocalLogTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class LocalLogTest {
 
+    @Test
     public void testA_localTimestamps() {
         boolean localTimestamps = true;
         doTestA(localTimestamps);
     }
 
+    @Test
     public void testA_nonLocalTimestamps() {
         boolean localTimestamps = false;
         doTestA(localTimestamps);
@@ -49,6 +56,7 @@
         testcase(new LocalLog(10, localTimestamps), lines, want);
     }
 
+    @Test
     public void testB() {
         String[] lines = {
             "foo",
@@ -59,6 +67,7 @@
         testcase(new LocalLog(0), lines, want);
     }
 
+    @Test
     public void testC() {
         String[] lines = {
             "dropped",
diff --git a/core/tests/utiltests/src/android/util/SlogTest.java b/core/tests/utiltests/src/android/util/SlogTest.java
new file mode 100644
index 0000000..6f761e3
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/SlogTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SlogTest {
+    private static final String TAG = "tag";
+    private static final String MSG = "msg";
+    private static final Throwable THROWABLE = new Throwable();
+
+    @Test
+    public void testSimple() {
+        Slog.v(TAG, MSG);
+        Slog.d(TAG, MSG);
+        Slog.i(TAG, MSG);
+        Slog.w(TAG, MSG);
+        Slog.e(TAG, MSG);
+    }
+
+    @Test
+    public void testThrowable() {
+        Slog.v(TAG, MSG, THROWABLE);
+        Slog.d(TAG, MSG, THROWABLE);
+        Slog.i(TAG, MSG, THROWABLE);
+        Slog.w(TAG, MSG, THROWABLE);
+        Slog.e(TAG, MSG, THROWABLE);
+    }
+}
diff --git a/core/tests/utiltests/src/android/util/TimeUtilsTest.java b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
new file mode 100644
index 0000000..e8246c8
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.function.Consumer;
+
+@RunWith(AndroidJUnit4.class)
+public class TimeUtilsTest {
+    public static final long SECOND_IN_MILLIS = 1000;
+    public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
+    public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
+    public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
+    public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
+
+    @Test
+    public void testFormatTime() {
+        assertEquals("1672556400000 (now)",
+                TimeUtils.formatTime(1672556400000L, 1672556400000L));
+        assertEquals("1672556400000 (in 10 ms)",
+                TimeUtils.formatTime(1672556400000L, 1672556400000L - 10));
+        assertEquals("1672556400000 (10 ms ago)",
+                TimeUtils.formatTime(1672556400000L, 1672556400000L + 10));
+
+        // Uses formatter above, so we just care that it doesn't crash
+        TimeUtils.formatRealtime(1672556400000L);
+        TimeUtils.formatUptime(1672556400000L);
+    }
+
+    @Test
+    public void testFormatDuration_Zero() {
+        assertEquals("0", TimeUtils.formatDuration(0));
+    }
+
+    @Test
+    public void testFormatDuration_Negative() {
+        assertEquals("-10ms", TimeUtils.formatDuration(-10));
+    }
+
+    @Test
+    public void testFormatDuration() {
+        long accum = 900;
+        assertEquals("+900ms", TimeUtils.formatDuration(accum));
+
+        accum += 59 * SECOND_IN_MILLIS;
+        assertEquals("+59s900ms", TimeUtils.formatDuration(accum));
+
+        accum += 59 * MINUTE_IN_MILLIS;
+        assertEquals("+59m59s900ms", TimeUtils.formatDuration(accum));
+
+        accum += 23 * HOUR_IN_MILLIS;
+        assertEquals("+23h59m59s900ms", TimeUtils.formatDuration(accum));
+
+        accum += 6 * DAY_IN_MILLIS;
+        assertEquals("+6d23h59m59s900ms", TimeUtils.formatDuration(accum));
+    }
+
+    @Test
+    public void testDumpTime() {
+        assertEquals("2023-01-01 00:00:00.000", runWithPrintWriter((pw) -> {
+            TimeUtils.dumpTime(pw, 1672556400000L);
+        }));
+        assertEquals("2023-01-01 00:00:00.000 (now)", runWithPrintWriter((pw) -> {
+            TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L);
+        }));
+        assertEquals("2023-01-01 00:00:00.000 (-10ms)", runWithPrintWriter((pw) -> {
+            TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L + 10);
+        }));
+    }
+
+    @Test
+    public void testFormatForLogging() {
+        assertEquals("unknown", TimeUtils.formatForLogging(0));
+        assertEquals("unknown", TimeUtils.formatForLogging(-1));
+        assertEquals("unknown", TimeUtils.formatForLogging(Long.MIN_VALUE));
+        assertEquals("2023-01-01 00:00:00", TimeUtils.formatForLogging(1672556400000L));
+    }
+
+    @Test
+    public void testLogTimeOfDay() {
+        assertEquals("01-01 00:00:00.000", TimeUtils.logTimeOfDay(1672556400000L));
+    }
+
+    public static String runWithPrintWriter(Consumer<PrintWriter> consumer) {
+        final StringWriter sw = new StringWriter();
+        consumer.accept(new PrintWriter(sw));
+        return sw.toString();
+    }
+
+    public static String runWithStringBuilder(Consumer<StringBuilder> consumer) {
+        final StringBuilder sb = new StringBuilder();
+        consumer.accept(sb);
+        return sb.toString();
+    }
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
index 07f1d4a..8dc9579 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
@@ -63,7 +63,7 @@
 
     @Override
     public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) {
-        final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree);
+        final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree, state);
         final String sourceCode = state.getSourceCode().toString();
         for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) {
             for (Tokens.Comment comment : token.comments()) {
@@ -112,9 +112,9 @@
     }
 
 
-    private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree) {
+    private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree, VisitorState state) {
         Map<Integer, Tree> javadoccableTrees = new HashMap<>();
-        new SuppressibleTreePathScanner<Void, Void>() {
+        new SuppressibleTreePathScanner<Void, Void>(state) {
             @Override
             public Void visitClass(ClassTree classTree, Void unused) {
                 javadoccableTrees.put(getStartPosition(classTree), classTree);
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index c366ccd..4d2d960 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -42,3 +42,11 @@
     description: "Enables PiP UI state callback on entering"
     bug: "303718131"
 }
+
+flag {
+    name: "enable_pip2_implementation"
+    namespace: "multitasking"
+    description: "Enables the new implementation of PiP (PiP2)"
+    bug: "290220798"
+    is_fixed_read_only: true
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index c7ab6aa..f5b877a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -963,7 +963,11 @@
                 && mTaskView.isAttachedToWindow()) {
             // post this to the looper, because if the device orientation just changed, we need to
             // let the current shell transition complete before updating the task view bounds.
-            post(() -> mTaskView.onLocationChanged());
+            post(() -> {
+                if (mTaskView != null) {
+                    mTaskView.onLocationChanged();
+                }
+            });
         }
         if (mIsOverflow) {
             // post this to the looper so that the view has a chance to be laid out before it can
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index 108aa82..1e30d8f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -21,13 +21,13 @@
 import android.content.ComponentName
 import android.content.Context
 import android.os.RemoteException
-import android.os.SystemProperties
 import android.util.DisplayMetrics
 import android.util.Log
 import android.util.Pair
 import android.util.TypedValue
 import android.window.TaskSnapshot
 import com.android.internal.protolog.common.ProtoLog
+import com.android.wm.shell.Flags
 import com.android.wm.shell.protolog.ShellProtoLogGroup
 import kotlin.math.abs
 
@@ -37,7 +37,6 @@
 
     // Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
     private const val EPSILON = 1e-7
-    private const val ENABLE_PIP2_IMPLEMENTATION = "persist.wm.debug.enable_pip2_implementation"
 
     /**
      * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
@@ -138,5 +137,5 @@
 
     @JvmStatic
     val isPip2ExperimentEnabled: Boolean
-        get() = SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false)
+        get() = Flags.enablePip2Implementation()
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 27dc870..b158f88 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -250,10 +250,10 @@
             SyncTransactionQueue syncQueue,
             @ShellMainThread ShellExecutor mainExecutor,
             Lazy<Transitions> transitionsLazy,
-            DockStateReader dockStateReader,
-            CompatUIConfiguration compatUIConfiguration,
-            CompatUIShellCommandHandler compatUIShellCommandHandler,
-            AccessibilityManager accessibilityManager) {
+            Lazy<DockStateReader> dockStateReader,
+            Lazy<CompatUIConfiguration> compatUIConfiguration,
+            Lazy<CompatUIShellCommandHandler> compatUIShellCommandHandler,
+            Lazy<AccessibilityManager> accessibilityManager) {
         if (!context.getResources().getBoolean(R.bool.config_enableCompatUIController)) {
             return Optional.empty();
         }
@@ -268,10 +268,10 @@
                         syncQueue,
                         mainExecutor,
                         transitionsLazy,
-                        dockStateReader,
-                        compatUIConfiguration,
-                        compatUIShellCommandHandler,
-                        accessibilityManager));
+                        dockStateReader.get(),
+                        compatUIConfiguration.get(),
+                        compatUIShellCommandHandler.get(),
+                        accessibilityManager.get()));
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index b528089d15..5c02dbc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.transition;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 
 import static com.android.wm.shell.transition.Transitions.TransitionObserver;
 
@@ -61,9 +62,10 @@
             }
 
             final int mode = change.getMode();
+            final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
             if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME
-                    && TransitionUtil.isOpenOrCloseMode(mode)) {
-                notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode));
+                    && (TransitionUtil.isOpenOrCloseMode(mode) || isBackGesture)) {
+                notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode) || isBackGesture);
             }
         }
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index a5629c8..b355ab0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -14,8 +14,6 @@
 import android.window.TransitionInfo
 import android.window.TransitionInfo.FLAG_IS_WALLPAPER
 import androidx.test.filters.SmallTest
-import com.android.server.testutils.any
-import com.android.server.testutils.mock
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestRunningTaskInfoBuilder
@@ -28,8 +26,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.verifyZeroInteractions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index ea7c0d9..421c445 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -18,8 +18,10 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
@@ -145,6 +147,26 @@
         verify(mListener, times(0)).onHomeVisibilityChanged(anyBoolean());
     }
 
+    @Test
+    public void testHomeActivityWithBackGestureNotifiesHomeIsVisible() throws RemoteException {
+        TransitionInfo info = mock(TransitionInfo.class);
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+
+        when(change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)).thenReturn(true);
+        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_CHANGE);
+
+        mHomeTransitionObserver.onTransitionReady(mock(IBinder.class),
+                info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+
+        verify(mListener, times(1)).onHomeVisibilityChanged(true);
+    }
+
+
     /**
      * Helper class to initialize variables for the rest.
      */
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index d056248..8748dab 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -603,7 +603,7 @@
     std::unique_ptr<Asset> asset = assets->GetAssetsProvider()->Open(filename, mode);
     if (asset) {
       if (out_cookie != nullptr) {
-        *out_cookie = i;
+        *out_cookie = i - 1;
       }
       return asset;
     }
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index b63ee1b..a9d1a2a 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -25,8 +25,9 @@
 
 #include "MinikinSkia.h"
 #include "SkPaint.h"
-#include "SkStream.h"  // Fot tests.
+#include "SkStream.h"  // For tests.
 #include "SkTypeface.h"
+#include "utils/TypefaceUtils.h"
 
 #include <minikin/FontCollection.h>
 #include <minikin/FontFamily.h>
@@ -186,7 +187,9 @@
     LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", kRobotoFont);
     void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
     std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data, st.st_size));
-    sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(std::move(fontData));
+    sk_sp<SkFontMgr> fm = android::FreeTypeFontMgr();
+    LOG_ALWAYS_FATAL_IF(fm == nullptr, "Could not load FreeType SkFontMgr");
+    sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(fontData));
     LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont);
 
     std::shared_ptr<minikin::MinikinFont> font =
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index de842e6..9f63dfd 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1065,17 +1065,8 @@
      * @see #isVolumeFixed()
      */
     public void adjustVolume(int direction, @PublicVolumeFlags int flags) {
-        if (autoPublicVolumeApiHardening()) {
-            final IAudioService service = getService();
-            try {
-                service.adjustVolume(direction, flags);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        } else {
-            MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
-            helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
-        }
+        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
+        helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
     }
 
     /**
@@ -1104,17 +1095,8 @@
      */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType,
             @PublicVolumeFlags int flags) {
-        if (autoPublicVolumeApiHardening()) {
-            final IAudioService service = getService();
-            try {
-                service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        } else {
-            MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
-            helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
-        }
+        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
+        helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
     }
 
     /** @hide */
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 11e3a08..b4ca485 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -500,10 +500,6 @@
             in String packageName, int uid, int pid, in UserHandle userHandle,
             int targetSdkVersion);
 
-    oneway void adjustVolume(int direction, int flags);
-
-    oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags);
-
     boolean isMusicActive(in boolean remotely);
 
     int getDeviceMaskForStream(in int streamType);
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
index 4533db6..3abdb6f 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
@@ -37,17 +37,17 @@
         RequestInfo::class.java
     )
 
-val Intent.getCredentialProviderDataList: List<ProviderData>
+val Intent.getCredentialProviderDataList: List<GetCredentialProviderData>
     get() = this.extras?.getParcelableArrayList(
         ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
         GetCredentialProviderData::class.java
-    ) ?: emptyList()
+    ) ?.filterIsInstance<GetCredentialProviderData>() ?: emptyList()
 
-val Intent.createCredentialProviderDataList: List<ProviderData>
+val Intent.createCredentialProviderDataList: List<CreateCredentialProviderData>
     get() = this.extras?.getParcelableArrayList(
         ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
         CreateCredentialProviderData::class.java
-    ) ?: emptyList()
+    ) ?.filterIsInstance<CreateCredentialProviderData>() ?: emptyList()
 
 val Intent.resultReceiver: ResultReceiver?
     get() = this.getParcelableExtra(
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
index ee45fbb..d4bca2a 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -18,7 +18,6 @@
 
 import android.content.Intent
 import android.credentials.ui.Entry
-import android.credentials.ui.GetCredentialProviderData
 import androidx.credentials.provider.PasswordCredentialEntry
 import com.android.credentialmanager.factory.fromSlice
 import com.android.credentialmanager.ktx.getCredentialProviderDataList
@@ -32,12 +31,10 @@
 fun Intent.toGet(): Request.Get {
     val credentialEntries = mutableListOf<Pair<String, Entry>>()
     for (providerData in getCredentialProviderDataList) {
-        if (providerData is GetCredentialProviderData) {
-            for (credentialEntry in providerData.credentialEntries) {
-                credentialEntries.add(
-                    Pair(providerData.providerFlattenedComponentName, credentialEntry)
-                )
-            }
+        for (credentialEntry in providerData.credentialEntries) {
+            credentialEntries.add(
+                Pair(providerData.providerFlattenedComponentName, credentialEntry)
+            )
         }
     }
 
diff --git a/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm b/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm
index 93a5082..071f9f4 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm
@@ -116,6 +116,9 @@
     base:                               'w'
     shift, capslock:                    'W'
     shift+capslock:                     'w'
+    ralt:                               '\u1e83'
+    shift+ralt, capslock+ralt:          '\u1e82'
+    shift+capslock+ralt:                '\u1e83'
 }
 
 key E {
@@ -147,6 +150,9 @@
     base:                               'y'
     shift, capslock:                    'Y'
     shift+capslock:                     'y'
+    ralt:                               '\u00fd'
+    shift+ralt, capslock+ralt:          '\u00dd'
+    shift+capslock+ralt:                '\u00fd'
 }
 
 key U {
@@ -313,6 +319,9 @@
     base:                               'c'
     shift, capslock:                    'C'
     shift+capslock:                     'c'
+    ralt:                               '\u00e7'
+    shift+ralt, capslock+ralt:          '\u00c7'
+    shift+capslock+ralt:                '\u00e7'
 }
 
 key V {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_french.kcm b/packages/InputDevices/res/raw/keyboard_layout_french.kcm
index 4906304..636f98d 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_french.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_french.kcm
@@ -44,7 +44,7 @@
     label:                              '2'
     base:                               '\u00e9'
     shift:                              '2'
-    ralt:                               '~'
+    ralt:                               '\u0303'
 }
 
 key 3 {
@@ -79,7 +79,7 @@
     label:                              '7'
     base:                               '\u00e8'
     shift:                              '7'
-    ralt:                               '`'
+    ralt:                               '\u0300'
 }
 
 key 8 {
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 7f23f74..ee49b23 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -94,7 +94,7 @@
         android:name="keyboard_layout_swiss_german"
         android:label="@string/keyboard_layout_swiss_german_label"
         android:keyboardLayout="@raw/keyboard_layout_swiss_german"
-        android:keyboardLocale="de-Latn-CH"
+        android:keyboardLocale="de-Latn-CH|gsw-Latn-CH"
         android:keyboardLayoutType="qwertz" />
 
     <keyboard-layout
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
index f83e37b..3774b88 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
@@ -37,9 +37,7 @@
 import java.util.List;
 import java.util.concurrent.Executor;
 
-/**
- * VolumeControlProfile handles Bluetooth Volume Control Controller role
- */
+/** VolumeControlProfile handles Bluetooth Volume Control Controller role */
 public class VolumeControlProfile implements LocalBluetoothProfile {
     private static final String TAG = "VolumeControlProfile";
     private static boolean DEBUG = true;
@@ -77,8 +75,8 @@
                     }
                     device = mDeviceManager.addDevice(nextDevice);
                 }
-                device.onProfileStateChanged(VolumeControlProfile.this,
-                        BluetoothProfile.STATE_CONNECTED);
+                device.onProfileStateChanged(
+                        VolumeControlProfile.this, BluetoothProfile.STATE_CONNECTED);
                 device.refresh();
             }
 
@@ -95,32 +93,36 @@
         }
     }
 
-    VolumeControlProfile(Context context, CachedBluetoothDeviceManager deviceManager,
+    VolumeControlProfile(
+            Context context,
+            CachedBluetoothDeviceManager deviceManager,
             LocalBluetoothProfileManager profileManager) {
         mContext = context;
         mDeviceManager = deviceManager;
         mProfileManager = profileManager;
 
-        BluetoothAdapter.getDefaultAdapter().getProfileProxy(context,
-                new VolumeControlProfile.VolumeControlProfileServiceListener(),
-                BluetoothProfile.VOLUME_CONTROL);
+        BluetoothAdapter.getDefaultAdapter()
+                .getProfileProxy(
+                        context,
+                        new VolumeControlProfile.VolumeControlProfileServiceListener(),
+                        BluetoothProfile.VOLUME_CONTROL);
     }
 
-
     /**
-     * Registers a {@link BluetoothVolumeControl.Callback} that will be invoked during the
-     * operation of this profile.
+     * Registers a {@link BluetoothVolumeControl.Callback} that will be invoked during the operation
+     * of this profile.
      *
-     * Repeated registration of the same <var>callback</var> object will have no effect after
-     * the first call to this method, even when the <var>executor</var> is different. API caller
-     * would have to call {@link #unregisterCallback(BluetoothVolumeControl.Callback)} with
-     * the same callback object before registering it again.
+     * <p>Repeated registration of the same <var>callback</var> object will have no effect after the
+     * first call to this method, even when the <var>executor</var> is different. API caller would
+     * have to call {@link #unregisterCallback(BluetoothVolumeControl.Callback)} with the same
+     * callback object before registering it again.
      *
      * @param executor an {@link Executor} to execute given callback
      * @param callback user implementation of the {@link BluetoothVolumeControl.Callback}
      * @throws IllegalArgumentException if a null executor or callback is given
      */
-    public void registerCallback(@NonNull @CallbackExecutor Executor executor,
+    public void registerCallback(
+            @NonNull @CallbackExecutor Executor executor,
             @NonNull BluetoothVolumeControl.Callback callback) {
         if (mService == null) {
             Log.w(TAG, "Proxy not attached to service. Cannot register callback.");
@@ -131,8 +133,9 @@
 
     /**
      * Unregisters the specified {@link BluetoothVolumeControl.Callback}.
-     * <p>The same {@link BluetoothVolumeControl.Callback} object used when calling
-     * {@link #registerCallback(Executor, BluetoothVolumeControl.Callback)} must be used.
+     *
+     * <p>The same {@link BluetoothVolumeControl.Callback} object used when calling {@link
+     * #registerCallback(Executor, BluetoothVolumeControl.Callback)} must be used.
      *
      * <p>Callbacks are automatically unregistered when application process goes away
      *
@@ -153,8 +156,8 @@
      * @param device {@link BluetoothDevice} representing the remote device
      * @param volumeOffset volume offset to be set on the remote device
      */
-    public void setVolumeOffset(BluetoothDevice device,
-            @IntRange(from = -255, to = 255) int volumeOffset) {
+    public void setVolumeOffset(
+            BluetoothDevice device, @IntRange(from = -255, to = 255) int volumeOffset) {
         if (mService == null) {
             Log.w(TAG, "Proxy not attached to service. Cannot set volume offset.");
             return;
@@ -165,16 +168,13 @@
         }
         mService.setVolumeOffset(device, volumeOffset);
     }
-
     /**
-     * Provides information about the possibility to set volume offset on the remote device.
-     * If the remote device supports Volume Offset Control Service, it is automatically
-     * connected.
+     * Provides information about the possibility to set volume offset on the remote device. If the
+     * remote device supports Volume Offset Control Service, it is automatically connected.
      *
      * @param device {@link BluetoothDevice} representing the remote device
      * @return {@code true} if volume offset function is supported and available to use on the
-     *         remote device. When Bluetooth is off, the return value should always be
-     *         {@code false}.
+     *     remote device. When Bluetooth is off, the return value should always be {@code false}.
      */
     public boolean isVolumeOffsetAvailable(BluetoothDevice device) {
         if (mService == null) {
@@ -188,6 +188,28 @@
         return mService.isVolumeOffsetAvailable(device);
     }
 
+    /**
+     * Tells the remote device to set a volume.
+     *
+     * @param device {@link BluetoothDevice} representing the remote device
+     * @param volume volume to be set on the remote device
+     * @param isGroupOp whether to set the volume to remote devices within the same CSIP group
+     */
+    public void setDeviceVolume(
+            BluetoothDevice device,
+            @IntRange(from = 0, to = 255) int volume,
+            boolean isGroupOp) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot set volume offset.");
+            return;
+        }
+        if (device == null) {
+            Log.w(TAG, "Device is null. Cannot set volume offset.");
+            return;
+        }
+        mService.setDeviceVolume(device, volume, isGroupOp);
+    }
+
     @Override
     public boolean accessProfileEnabled() {
         return false;
@@ -199,10 +221,9 @@
     }
 
     /**
-     * Gets VolumeControlProfile devices matching connection states{
-     * {@code BluetoothProfile.STATE_CONNECTED},
-     * {@code BluetoothProfile.STATE_CONNECTING},
-     * {@code BluetoothProfile.STATE_DISCONNECTING}}
+     * Gets VolumeControlProfile devices matching connection states{ {@code
+     * BluetoothProfile.STATE_CONNECTED}, {@code BluetoothProfile.STATE_CONNECTING}, {@code
+     * BluetoothProfile.STATE_DISCONNECTING}}
      *
      * @return Matching device list
      */
@@ -211,8 +232,11 @@
             return new ArrayList<BluetoothDevice>(0);
         }
         return mService.getDevicesMatchingConnectionStates(
-                new int[]{BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING,
-                        BluetoothProfile.STATE_DISCONNECTING});
+                new int[] {
+                    BluetoothProfile.STATE_CONNECTED,
+                    BluetoothProfile.STATE_CONNECTING,
+                    BluetoothProfile.STATE_DISCONNECTING
+                });
     }
 
     @Override
@@ -285,7 +309,7 @@
 
     @Override
     public int getSummaryResourceForDevice(BluetoothDevice device) {
-        return 0;   // VCP profile not displayed in UI
+        return 0; // VCP profile not displayed in UI
     }
 
     @Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java
index c560627..fe1529d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java
@@ -54,18 +54,14 @@
 public class VolumeControlProfileTest {
 
     private static final int TEST_VOLUME_OFFSET = 10;
+    private static final int TEST_VOLUME_VALUE = 10;
 
-    @Rule
-    public final MockitoRule mockito = MockitoJUnit.rule();
+    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
 
-    @Mock
-    private CachedBluetoothDeviceManager mDeviceManager;
-    @Mock
-    private LocalBluetoothProfileManager mProfileManager;
-    @Mock
-    private BluetoothDevice mBluetoothDevice;
-    @Mock
-    private BluetoothVolumeControl mService;
+    @Mock private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock private LocalBluetoothProfileManager mProfileManager;
+    @Mock private BluetoothDevice mBluetoothDevice;
+    @Mock private BluetoothVolumeControl mService;
 
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private BluetoothProfile.ServiceListener mServiceListener;
@@ -177,14 +173,14 @@
     @Test
     public void getConnectedDevices_returnCorrectList() {
         mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
-        int[] connectedStates = new int[] {
-                BluetoothProfile.STATE_CONNECTED,
-                BluetoothProfile.STATE_CONNECTING,
-                BluetoothProfile.STATE_DISCONNECTING};
-        List<BluetoothDevice> connectedList = Arrays.asList(
-                mBluetoothDevice,
-                mBluetoothDevice,
-                mBluetoothDevice);
+        int[] connectedStates =
+                new int[] {
+                    BluetoothProfile.STATE_CONNECTED,
+                    BluetoothProfile.STATE_CONNECTING,
+                    BluetoothProfile.STATE_DISCONNECTING
+                };
+        List<BluetoothDevice> connectedList =
+                Arrays.asList(mBluetoothDevice, mBluetoothDevice, mBluetoothDevice);
         when(mService.getDevicesMatchingConnectionStates(connectedStates))
                 .thenReturn(connectedList);
 
@@ -222,6 +218,16 @@
     }
 
     @Test
+    public void setDeviceVolume_verifyIsCalled() {
+        mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
+
+        mProfile.setDeviceVolume(mBluetoothDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true);
+
+        verify(mService)
+                .setDeviceVolume(mBluetoothDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true);
+    }
+
+    @Test
     public void isVolumeOffsetAvailable_verifyIsCalledAndReturnTrue() {
         mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
         when(mService.isVolumeOffsetAvailable(mBluetoothDevice)).thenReturn(true);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 2c15fc6..8412cba 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -249,6 +249,8 @@
         Settings.Secure.HUB_MODE_TUTORIAL_STATE,
         Settings.Secure.STYLUS_BUTTONS_ENABLED,
         Settings.Secure.STYLUS_HANDWRITING_ENABLED,
-        Settings.Secure.DEFAULT_NOTE_TASK_PROFILE
+        Settings.Secure.DEFAULT_NOTE_TASK_PROFILE,
+        Settings.Secure.CREDENTIAL_SERVICE,
+        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 71c2ddc..9197554 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -19,11 +19,13 @@
 import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.AUTOFILL_SERVICE_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_COMPONENT_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_PACKAGE_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.COMMA_SEPARATED_COMPONENT_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.JSON_OBJECT_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR;
@@ -62,7 +64,6 @@
         VALIDATORS.put(Secure.ADAPTIVE_CHARGING_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.CAMERA_AUTOROTATE, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.AUTOFILL_SERVICE, NULLABLE_COMPONENT_NAME_VALIDATOR);
         VALIDATORS.put(
                 Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
                 new InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE));
@@ -398,5 +399,8 @@
         VALIDATORS.put(Secure.STYLUS_HANDWRITING_ENABLED,
                 new DiscreteValueValidator(new String[] {"-1", "0", "1"}));
         VALIDATORS.put(Secure.DEFAULT_NOTE_TASK_PROFILE, NON_NEGATIVE_INTEGER_VALIDATOR);
+        VALIDATORS.put(Secure.CREDENTIAL_SERVICE, CREDENTIAL_SERVICE_VALIDATOR);
+        VALIDATORS.put(Secure.CREDENTIAL_SERVICE_PRIMARY, NULLABLE_COMPONENT_NAME_VALIDATOR);
+        VALIDATORS.put(Secure.AUTOFILL_SERVICE, AUTOFILL_SERVICE_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index 49012b0..a8a659e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -235,4 +235,30 @@
             }
         }
     };
+
+    static final Validator CREDENTIAL_SERVICE_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(String value) {
+            if (value == null || value.equals("")) {
+                return true;
+            }
+
+            return COLON_SEPARATED_COMPONENT_LIST_VALIDATOR.validate(value);
+        }
+    };
+
+    static final Validator AUTOFILL_SERVICE_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(String value) {
+            if (value == null || value.equals("")) {
+                return true;
+            }
+
+            if (value.equals("credential-provider")) {
+               return true;
+            }
+
+            return NULLABLE_COMPONENT_NAME_VALIDATOR.validate(value);
+        }
+    };
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index f1b53ed..efed8c3 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -851,8 +851,6 @@
                  Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT,
                  Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
                  Settings.Secure.UI_TRANSLATION_ENABLED,
-                 Settings.Secure.CREDENTIAL_SERVICE,
-                 Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
                  Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED,
                  Settings.Secure.DND_CONFIGS_MIGRATED,
                  Settings.Secure.NAVIGATION_MODE_RESTORE);
diff --git a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
index 865f431..3b3bf8ca 100644
--- a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
@@ -340,6 +340,60 @@
         failIfOffendersPresent(offenders, "Settings.Secure");
     }
 
+    @Test
+    public void testCredentialServiceValidator_returnsTrueIfNull() {
+        assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate(null));
+    }
+
+    @Test
+    public void testCredentialServiceValidator_returnsTrueIfEmpty() {
+        assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate(""));
+    }
+
+    @Test
+    public void testCredentialServiceValidator_returnsTrueIfSingleComponentName() {
+        assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate(
+            "android.credentials/android.credentials.Test"));
+    }
+
+    @Test
+    public void testCredentialServiceValidator_returnsTrueIfMultipleComponentName() {
+        assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate(
+            "android.credentials/android.credentials.Test"
+            + ":android.credentials/.Test2"));
+    }
+
+    @Test
+    public void testCredentialServiceValidator_returnsFalseIfInvalidComponentName() {
+        assertFalse(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate("test"));
+    }
+
+    @Test
+    public void testAutofillServiceValidator_returnsTrueIfNull() {
+        assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate(null));
+    }
+
+    @Test
+    public void testAutofillServiceValidator_returnsTrueIfEmpty() {
+        assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate(""));
+    }
+
+    @Test
+    public void testAutofillServiceValidator_returnsTrueIfPlaceholder() {
+        assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate("credential-provider"));
+    }
+
+    @Test
+    public void testAutofillServiceValidator_returnsTrueIfSingleComponentName() {
+        assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate(
+            "android.credentials/android.credentials.Test"));
+    }
+
+    @Test
+    public void testAutofillServiceValidator_returnsFalseIfInvalidComponentName() {
+        assertFalse(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate("test"));
+    }
+
     private void failIfOffendersPresent(String offenders, String settingsType) {
         if (offenders.length() > 0) {
             fail("All " + settingsType + " settings that are backed up have to have a non-null"
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 68d4b63..a745ab5 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -140,4 +140,11 @@
     namespace: "systemui"
     description: "Move LightRevealScrim to recommended architecture"
     bug: "281655028"
-}
\ No newline at end of file
+}
+
+flag {
+   name: "media_in_scene_container"
+   namespace: "systemui"
+   description: "Enable media in the scene container framework"
+   bug: "296122467"
+}
diff --git a/packages/SystemUI/communal/layout/Android.bp b/packages/SystemUI/communal/layout/Android.bp
deleted file mode 100644
index 88dad66..0000000
--- a/packages/SystemUI/communal/layout/Android.bp
+++ /dev/null
@@ -1,35 +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 {
-    default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
-}
-
-android_library {
-    name: "CommunalLayoutLib",
-    srcs: [
-        "src/**/*.kt",
-    ],
-    static_libs: [
-        "androidx.arch.core_core-runtime",
-        "androidx.compose.animation_animation-graphics",
-        "androidx.compose.runtime_runtime",
-        "androidx.compose.material3_material3",
-        "jsr330",
-        "kotlinx-coroutines-android",
-        "kotlinx-coroutines-core",
-    ],
-    manifest: "AndroidManifest.xml",
-    kotlincflags: ["-Xjvm-default=all"],
-}
diff --git a/packages/SystemUI/communal/layout/AndroidManifest.xml b/packages/SystemUI/communal/layout/AndroidManifest.xml
deleted file mode 100644
index 141be07..0000000
--- a/packages/SystemUI/communal/layout/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- Copyright (C) 2023 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-
-<manifest package="com.android.systemui.communal.layout" />
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt
deleted file mode 100644
index 91fe33c..0000000
--- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.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.communal.layout
-
-import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
-
-/** Computes the arrangement of cards. */
-class CommunalLayoutEngine {
-    companion object {
-        /**
-         * Determines the size that each card should be rendered in, and distributes the cards into
-         * columns.
-         *
-         * Returns a nested list where the outer list contains columns, and the inner list contains
-         * cards in each column.
-         *
-         * Currently treats the first supported size as the size to be rendered in, ignoring other
-         * supported sizes.
-         *
-         * Cards are ordered by priority, from highest to lowest.
-         */
-        fun distributeCardsIntoColumns(
-            cards: List<CommunalGridLayoutCard>,
-        ): List<List<CommunalGridLayoutCardInfo>> {
-            val result = ArrayList<ArrayList<CommunalGridLayoutCardInfo>>()
-
-            var capacityOfLastColumn = 0
-            val sorted = cards.sortedByDescending { it.priority }
-            for (card in sorted) {
-                val cardSize = card.supportedSizes.first()
-                if (capacityOfLastColumn >= cardSize.value) {
-                    // Card fits in last column
-                    capacityOfLastColumn -= cardSize.value
-                } else {
-                    // Create a new column
-                    result.add(arrayListOf())
-                    capacityOfLastColumn = CommunalGridLayoutCard.Size.FULL.value - cardSize.value
-                }
-
-                result.last().add(CommunalGridLayoutCardInfo(card, cardSize))
-            }
-
-            return result
-        }
-    }
-
-    /**
-     * A data class that wraps around a [CommunalGridLayoutCard] and also contains the size that the
-     * card should be rendered in.
-     */
-    data class CommunalGridLayoutCardInfo(
-        val card: CommunalGridLayoutCard,
-        val size: CommunalGridLayoutCard.Size,
-    )
-}
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt
deleted file mode 100644
index 33024f7..0000000
--- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.layout.ui.compose
-
-import android.util.SizeF
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.lazy.LazyRow
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import com.android.systemui.communal.layout.CommunalLayoutEngine
-import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
-import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig
-
-/**
- * An arrangement of cards with a horizontal scroll, where each card is displayed in the right size
- * and follows a specific order based on its priority, ensuring a seamless layout without any gaps.
- */
-@Composable
-fun CommunalGridLayout(
-    modifier: Modifier,
-    layoutConfig: CommunalGridLayoutConfig,
-    communalCards: List<CommunalGridLayoutCard>,
-) {
-    val columns = CommunalLayoutEngine.distributeCardsIntoColumns(communalCards)
-    LazyRow(
-        modifier = modifier.height(layoutConfig.gridHeight),
-        horizontalArrangement = Arrangement.spacedBy(layoutConfig.gridGutter),
-    ) {
-        for (column in columns) {
-            item {
-                Column(
-                    modifier = Modifier.width(layoutConfig.cardWidth),
-                    verticalArrangement = Arrangement.spacedBy(layoutConfig.gridGutter),
-                ) {
-                    for (cardInfo in column) {
-                        Row(
-                            modifier = Modifier.height(layoutConfig.cardHeight(cardInfo.size)),
-                        ) {
-                            cardInfo.card.Content(
-                                modifier = Modifier.fillMaxSize(),
-                                size =
-                                    SizeF(
-                                        layoutConfig.cardWidth.value,
-                                        layoutConfig.cardHeight(cardInfo.size).value,
-                                    ),
-                            )
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt
deleted file mode 100644
index 4b2a156..0000000
--- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.layout.ui.compose.config
-
-import android.util.SizeF
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-
-/** A card that hosts content to be rendered in the communal grid layout. */
-abstract class CommunalGridLayoutCard {
-    /**
-     * Content to be hosted by the card.
-     *
-     * To host non-Compose views, see
-     * https://developer.android.com/jetpack/compose/migrate/interoperability-apis/views-in-compose.
-     *
-     * @param size The size given to the card. Content of the card should fill all this space, given
-     *   that margins and paddings have been taken care of by the layout.
-     */
-    @Composable abstract fun Content(modifier: Modifier, size: SizeF)
-
-    /**
-     * Sizes supported by the card.
-     *
-     * If multiple sizes are available, they should be ranked in order of preference, from most to
-     * least preferred.
-     */
-    abstract val supportedSizes: List<Size>
-
-    /**
-     * Priority of the content hosted by the card.
-     *
-     * The value of priority is relative to other cards. Cards with a higher priority are generally
-     * ordered first.
-     */
-    open val priority: Int = 0
-
-    /**
-     * Size of the card.
-     *
-     * @param value A numeric value that represents the size. Must be less than or equal to
-     *   [Size.FULL].
-     */
-    enum class Size(val value: Int) {
-        /** The card takes up full height of the grid layout. */
-        FULL(value = 6),
-
-        /** The card takes up half of the vertical space of the grid layout. */
-        HALF(value = 3),
-
-        /** The card takes up a third of the vertical space of the grid layout. */
-        THIRD(value = 2),
-    }
-}
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt
deleted file mode 100644
index 143df83..0000000
--- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.layout.ui.compose.config
-
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.times
-
-/**
- * Configurations of the communal grid layout.
- *
- * The communal grid layout follows Material Design's responsive layout grid (see
- * https://m2.material.io/design/layout/responsive-layout-grid.html), in which the layout is divided
- * up by columns and gutters, and each card occupies one or multiple columns.
- */
-data class CommunalGridLayoutConfig(
-    /**
-     * Size in dp of each grid column.
-     *
-     * Every card occupies one or more grid columns, which means that the width of each card is
-     * influenced by the size of the grid columns.
-     */
-    val gridColumnSize: Dp,
-
-    /**
-     * Size in dp of each grid gutter.
-     *
-     * A gutter is the space between columns that helps separate content. This is, therefore, also
-     * the size of the gaps between cards, both horizontally and vertically.
-     */
-    val gridGutter: Dp,
-
-    /**
-     * Height in dp of the grid layout.
-     *
-     * Cards with a full size take up the entire height of the grid layout.
-     */
-    val gridHeight: Dp,
-
-    /**
-     * Number of grid columns that each card occupies.
-     *
-     * It's important to note that all the cards take up the same number of grid columns, or in
-     * simpler terms, they all have the same width.
-     */
-    val gridColumnsPerCard: Int,
-) {
-    /**
-     * Width in dp of each card.
-     *
-     * It's important to note that all the cards take up the same number of grid columns, or in
-     * simpler terms, they all have the same width.
-     */
-    val cardWidth = gridColumnSize * gridColumnsPerCard + gridGutter * (gridColumnsPerCard - 1)
-
-    /** Returns the height of a card in dp, based on its size. */
-    fun cardHeight(cardSize: CommunalGridLayoutCard.Size): Dp {
-        return when (cardSize) {
-            CommunalGridLayoutCard.Size.FULL -> cardHeightBy(denominator = 1)
-            CommunalGridLayoutCard.Size.HALF -> cardHeightBy(denominator = 2)
-            CommunalGridLayoutCard.Size.THIRD -> cardHeightBy(denominator = 3)
-        }
-    }
-
-    /** Returns the height of a card in dp when the layout is evenly divided by [denominator]. */
-    private fun cardHeightBy(denominator: Int): Dp {
-        return (gridHeight - (denominator - 1) * gridGutter) / denominator
-    }
-}
diff --git a/packages/SystemUI/communal/layout/tests/Android.bp b/packages/SystemUI/communal/layout/tests/Android.bp
deleted file mode 100644
index 9a05504..0000000
--- a/packages/SystemUI/communal/layout/tests/Android.bp
+++ /dev/null
@@ -1,47 +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 {
-    default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
-}
-
-android_test {
-    name: "CommunalLayoutLibTests",
-    srcs: [
-        "**/*.kt",
-    ],
-    static_libs: [
-        "CommunalLayoutLib",
-        "androidx.test.runner",
-        "androidx.test.rules",
-        "androidx.test.ext.junit",
-        "frameworks-base-testutils",
-        "junit",
-        "kotlinx_coroutines_test",
-        "mockito-target-extended-minus-junit4",
-        "platform-test-annotations",
-        "testables",
-        "truth",
-    ],
-    libs: [
-        "android.test.mock",
-        "android.test.base",
-        "android.test.runner",
-    ],
-    jni_libs: [
-        "libdexmakerjvmtiagent",
-        "libstaticjvmtiagent",
-    ],
-    manifest: "AndroidManifest.xml",
-}
diff --git a/packages/SystemUI/communal/layout/tests/AndroidManifest.xml b/packages/SystemUI/communal/layout/tests/AndroidManifest.xml
deleted file mode 100644
index b19007c..0000000
--- a/packages/SystemUI/communal/layout/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.systemui.communal.layout.tests">
-
-    <application android:debuggable="true" android:largeHeap="true">
-        <uses-library android:name="android.test.mock" />
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.testing.TestableInstrumentation"
-        android:targetPackage="com.android.systemui.communal.layout.tests"
-        android:label="Tests for CommunalLayoutLib">
-    </instrumentation>
-
-</manifest>
diff --git a/packages/SystemUI/communal/layout/tests/AndroidTest.xml b/packages/SystemUI/communal/layout/tests/AndroidTest.xml
deleted file mode 100644
index 1352b23..0000000
--- a/packages/SystemUI/communal/layout/tests/AndroidTest.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration description="Runs tests for CommunalLayoutLib">
-
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="install-arg" value="-t" />
-        <option name="test-file-name" value="CommunalLayoutLibTests.apk" />
-    </target_preparer>
-
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-suite-tag" value="framework-base-presubmit" />
-    <option name="test-tag" value="CommunalLayoutLibTests" />
-
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="com.android.systemui.communal.layout.tests" />
-        <option name="runner" value="android.testing.TestableInstrumentation" />
-        <option name="hidden-api-checks" value="false"/>
-    </test>
-
-</configuration>
diff --git a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt
deleted file mode 100644
index 50b7c5f..0000000
--- a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt
+++ /dev/null
@@ -1,145 +0,0 @@
-package com.android.systemui.communal.layout
-
-import android.util.SizeF
-import androidx.compose.material3.Card
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class CommunalLayoutEngineTest {
-    @Test
-    fun distribution_fullLayout() {
-        val cards =
-            listOf(
-                generateCard(CommunalGridLayoutCard.Size.FULL),
-                generateCard(CommunalGridLayoutCard.Size.HALF),
-                generateCard(CommunalGridLayoutCard.Size.HALF),
-                generateCard(CommunalGridLayoutCard.Size.THIRD),
-                generateCard(CommunalGridLayoutCard.Size.THIRD),
-                generateCard(CommunalGridLayoutCard.Size.THIRD),
-            )
-        val expected =
-            listOf(
-                listOf(
-                    CommunalGridLayoutCard.Size.FULL,
-                ),
-                listOf(
-                    CommunalGridLayoutCard.Size.HALF,
-                    CommunalGridLayoutCard.Size.HALF,
-                ),
-                listOf(
-                    CommunalGridLayoutCard.Size.THIRD,
-                    CommunalGridLayoutCard.Size.THIRD,
-                    CommunalGridLayoutCard.Size.THIRD,
-                ),
-            )
-
-        assertDistributionBySize(cards, expected)
-    }
-
-    @Test
-    fun distribution_layoutWithGaps() {
-        val cards =
-            listOf(
-                generateCard(CommunalGridLayoutCard.Size.HALF),
-                generateCard(CommunalGridLayoutCard.Size.THIRD),
-                generateCard(CommunalGridLayoutCard.Size.HALF),
-                generateCard(CommunalGridLayoutCard.Size.FULL),
-                generateCard(CommunalGridLayoutCard.Size.THIRD),
-            )
-        val expected =
-            listOf(
-                listOf(
-                    CommunalGridLayoutCard.Size.HALF,
-                    CommunalGridLayoutCard.Size.THIRD,
-                ),
-                listOf(
-                    CommunalGridLayoutCard.Size.HALF,
-                ),
-                listOf(
-                    CommunalGridLayoutCard.Size.FULL,
-                ),
-                listOf(
-                    CommunalGridLayoutCard.Size.THIRD,
-                ),
-            )
-
-        assertDistributionBySize(cards, expected)
-    }
-
-    @Test
-    fun distribution_sortByPriority() {
-        val cards =
-            listOf(
-                generateCard(priority = 2),
-                generateCard(priority = 7),
-                generateCard(priority = 10),
-                generateCard(priority = 1),
-                generateCard(priority = 5),
-            )
-        val expected =
-            listOf(
-                listOf(10, 7),
-                listOf(5, 2),
-                listOf(1),
-            )
-
-        assertDistributionByPriority(cards, expected)
-    }
-
-    private fun assertDistributionBySize(
-        cards: List<CommunalGridLayoutCard>,
-        expected: List<List<CommunalGridLayoutCard.Size>>,
-    ) {
-        val result = CommunalLayoutEngine.distributeCardsIntoColumns(cards)
-
-        for (c in expected.indices) {
-            for (r in expected[c].indices) {
-                assertThat(result[c][r].size).isEqualTo(expected[c][r])
-            }
-        }
-    }
-
-    private fun assertDistributionByPriority(
-        cards: List<CommunalGridLayoutCard>,
-        expected: List<List<Int>>,
-    ) {
-        val result = CommunalLayoutEngine.distributeCardsIntoColumns(cards)
-
-        for (c in expected.indices) {
-            for (r in expected[c].indices) {
-                assertThat(result[c][r].card.priority).isEqualTo(expected[c][r])
-            }
-        }
-    }
-
-    private fun generateCard(size: CommunalGridLayoutCard.Size): CommunalGridLayoutCard {
-        return object : CommunalGridLayoutCard() {
-            override val supportedSizes = listOf(size)
-
-            @Composable
-            override fun Content(modifier: Modifier, size: SizeF) {
-                Card(modifier = modifier, content = {})
-            }
-        }
-    }
-
-    private fun generateCard(priority: Int): CommunalGridLayoutCard {
-        return object : CommunalGridLayoutCard() {
-            override val supportedSizes = listOf(Size.HALF)
-            override val priority = priority
-
-            @Composable
-            override fun Content(modifier: Modifier, size: SizeF) {
-                Card(modifier = modifier, content = {})
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt
deleted file mode 100644
index 946eeec..0000000
--- a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.android.systemui.communal.layout.ui.compose.config
-
-import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class CommunalGridLayoutConfigTest {
-    @Test
-    fun cardWidth() {
-        Truth.assertThat(
-                CommunalGridLayoutConfig(
-                        gridColumnSize = 5.dp,
-                        gridGutter = 3.dp,
-                        gridHeight = 17.dp,
-                        gridColumnsPerCard = 1,
-                    )
-                    .cardWidth
-            )
-            .isEqualTo(5.dp)
-
-        Truth.assertThat(
-                CommunalGridLayoutConfig(
-                        gridColumnSize = 5.dp,
-                        gridGutter = 3.dp,
-                        gridHeight = 17.dp,
-                        gridColumnsPerCard = 2,
-                    )
-                    .cardWidth
-            )
-            .isEqualTo(13.dp)
-
-        Truth.assertThat(
-                CommunalGridLayoutConfig(
-                        gridColumnSize = 5.dp,
-                        gridGutter = 3.dp,
-                        gridHeight = 17.dp,
-                        gridColumnsPerCard = 3,
-                    )
-                    .cardWidth
-            )
-            .isEqualTo(21.dp)
-    }
-
-    @Test
-    fun cardHeight() {
-        val config =
-            CommunalGridLayoutConfig(
-                gridColumnSize = 5.dp,
-                gridGutter = 2.dp,
-                gridHeight = 10.dp,
-                gridColumnsPerCard = 3,
-            )
-
-        Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.FULL)).isEqualTo(10.dp)
-        Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.HALF)).isEqualTo(4.dp)
-        Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.THIRD)).isEqualTo(2.dp)
-    }
-}
diff --git a/packages/SystemUI/compose/features/Android.bp b/packages/SystemUI/compose/features/Android.bp
index 16c2437..796abf4b 100644
--- a/packages/SystemUI/compose/features/Android.bp
+++ b/packages/SystemUI/compose/features/Android.bp
@@ -31,7 +31,6 @@
     ],
 
     static_libs: [
-        "CommunalLayoutLib",
         "SystemUI-core",
         "PlatformComposeCore",
         "PlatformComposeSceneTransitionLayout",
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index ce84c19..2c4dc80 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -65,10 +65,13 @@
         viewModel.currentScene
             .transform<CommunalSceneKey, SceneKey> { value -> value.toTransitionSceneKey() }
             .collectAsState(TransitionSceneKey.Blank)
+    // Don't show hub mode UI if keyguard is present. This is important since we're in the shade,
+    // which can be opened from many locations.
+    val isKeyguardShowing by viewModel.isKeyguardVisible.collectAsState(initial = false)
 
     // Failsafe to hide the whole SceneTransitionLayout in case of bugginess.
     var showSceneTransitionLayout by remember { mutableStateOf(true) }
-    if (!showSceneTransitionLayout) {
+    if (!showSceneTransitionLayout || !isKeyguardShowing) {
         return
     }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 28a4801..7654683 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -25,9 +25,6 @@
 import androidx.compose.foundation.layout.defaultMinSize
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
@@ -36,7 +33,6 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
@@ -76,9 +72,6 @@
                 .element(QuickSettings.Elements.Content)
                 .fillMaxWidth()
                 .defaultMinSize(minHeight = 300.dp)
-                .clip(RoundedCornerShape(32.dp))
-                .background(MaterialTheme.colorScheme.primary)
-                .padding(1.dp),
     ) {
         QuickSettingsContent(qsSceneAdapter = qsSceneAdapter, state)
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 9dd7bfaf..871d9f9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -23,7 +23,7 @@
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
 import androidx.compose.animation.shrinkVertically
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
@@ -31,6 +31,7 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
@@ -51,6 +52,7 @@
 import com.android.systemui.scene.ui.composable.ComposableScene
 import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
 import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
+import com.android.systemui.shade.ui.composable.Shade
 import com.android.systemui.shade.ui.composable.ShadeHeader
 import com.android.systemui.statusbar.phone.StatusBarIconController
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager
@@ -104,53 +106,59 @@
 ) {
     // TODO(b/280887232): implement the real UI.
     Box(modifier = modifier.fillMaxSize()) {
-        val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
-        val collapsedHeaderHeight =
-            with(LocalDensity.current) { ShadeHeader.Dimensions.CollapsedHeight.roundToPx() }
-        Column(
-            horizontalAlignment = Alignment.CenterHorizontally,
-            modifier =
-                Modifier.fillMaxSize()
-                    .clickable(onClick = { viewModel.onContentClicked() })
-                    .padding(start = 16.dp, end = 16.dp, bottom = 48.dp)
-        ) {
-            when (LocalWindowSizeClass.current.widthSizeClass) {
-                WindowWidthSizeClass.Compact ->
-                    AnimatedVisibility(
-                        visible = !isCustomizing,
-                        enter =
-                            expandVertically(
-                                animationSpec = tween(1000),
-                                initialHeight = { collapsedHeaderHeight },
-                            ) + fadeIn(tween(1000)),
-                        exit =
-                            shrinkVertically(
-                                animationSpec = tween(1000),
-                                targetHeight = { collapsedHeaderHeight },
-                                shrinkTowards = Alignment.Top,
-                            ) + fadeOut(tween(1000)),
-                    ) {
-                        ExpandedShadeHeader(
+        Box(modifier = Modifier.fillMaxSize()) {
+            val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
+            val collapsedHeaderHeight =
+                with(LocalDensity.current) { ShadeHeader.Dimensions.CollapsedHeight.roundToPx() }
+            Spacer(
+                modifier =
+                    Modifier.element(Shade.Elements.ScrimBackground)
+                        .fillMaxSize()
+                        .background(MaterialTheme.colorScheme.scrim, shape = Shade.Shapes.Scrim)
+            )
+            Column(
+                horizontalAlignment = Alignment.CenterHorizontally,
+                modifier =
+                    Modifier.fillMaxSize().padding(start = 16.dp, end = 16.dp, bottom = 48.dp)
+            ) {
+                when (LocalWindowSizeClass.current.widthSizeClass) {
+                    WindowWidthSizeClass.Compact ->
+                        AnimatedVisibility(
+                            visible = !isCustomizing,
+                            enter =
+                                expandVertically(
+                                    animationSpec = tween(1000),
+                                    initialHeight = { collapsedHeaderHeight },
+                                ) + fadeIn(tween(1000)),
+                            exit =
+                                shrinkVertically(
+                                    animationSpec = tween(1000),
+                                    targetHeight = { collapsedHeaderHeight },
+                                    shrinkTowards = Alignment.Top,
+                                ) + fadeOut(tween(1000)),
+                        ) {
+                            ExpandedShadeHeader(
+                                viewModel = viewModel.shadeHeaderViewModel,
+                                createTintedIconManager = createTintedIconManager,
+                                createBatteryMeterViewController = createBatteryMeterViewController,
+                                statusBarIconController = statusBarIconController,
+                            )
+                        }
+                    else ->
+                        CollapsedShadeHeader(
                             viewModel = viewModel.shadeHeaderViewModel,
                             createTintedIconManager = createTintedIconManager,
                             createBatteryMeterViewController = createBatteryMeterViewController,
                             statusBarIconController = statusBarIconController,
                         )
-                    }
-                else ->
-                    CollapsedShadeHeader(
-                        viewModel = viewModel.shadeHeaderViewModel,
-                        createTintedIconManager = createTintedIconManager,
-                        createBatteryMeterViewController = createBatteryMeterViewController,
-                        statusBarIconController = statusBarIconController,
-                    )
+                }
+                Spacer(modifier = Modifier.height(16.dp))
+                QuickSettings(
+                    modifier = Modifier.fillMaxHeight(),
+                    viewModel.qsSceneAdapter,
+                    QSSceneAdapter.State.QS
+                )
             }
-            Spacer(modifier = Modifier.height(16.dp))
-            QuickSettings(
-                modifier = Modifier.fillMaxHeight(),
-                viewModel.qsSceneAdapter,
-                QSSceneAdapter.State.QS
-            )
         }
         HeadsUpNotificationSpace(
             viewModel = viewModel.notifications,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index f0e3c99..6434209 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -133,16 +133,6 @@
             boolean afterKeyguardGone,
             boolean deferred);
 
-    /** Execute a runnable after dismissing keyguard. */
-    void executeRunnableDismissingKeyguard(
-            Runnable runnable,
-            Runnable cancelAction,
-            boolean dismissShade,
-            boolean afterKeyguardGone,
-            boolean deferred,
-            boolean willAnimateOnKeyguard,
-            @Nullable String customMessage);
-
     /** Whether we should animate an activity launch. */
     boolean shouldAnimateLaunch(boolean isActivityIntent);
 
diff --git a/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml
index 952f056..cc99f5e 100644
--- a/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml
+++ b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml
@@ -22,13 +22,15 @@
     android:layout_width="wrap_content"
     android:layout_height="match_parent"
     android:gravity="center_vertical"
-    android:orientation="horizontal" >
+    android:orientation="horizontal"
+    android:theme="@style/Theme.SystemUI.QuickSettings.Header" >
 
     <com.android.systemui.util.AutoMarqueeTextView
         android:id="@+id/mobile_carrier_text"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_weight="1"
+        android:textAppearance="@style/TextAppearance.QS.Status.Carriers"
         android:layout_marginEnd="@dimen/qs_carrier_margin_width"
         android:visibility="gone"
         android:textDirection="locale"
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 355e75d..9c08f5e 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -18,7 +18,7 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
         android:color="?android:attr/colorControlHighlight">
-    <item>
+    <item android:id="@+id/notification_background_color_layer">
         <shape>
             <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
         </shape>
diff --git a/packages/SystemUI/res/layout/udfps_touch_overlay.xml b/packages/SystemUI/res/layout/udfps_touch_overlay.xml
new file mode 100644
index 0000000..ea92776
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_touch_overlay.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/udfps_touch_overlay"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:contentDescription="@string/accessibility_fingerprint_label">
+</com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay>
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index 3757274..1ee58de 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -317,7 +317,7 @@
         }
 
         keyguardUpdateMonitor?.let {
-            val anyFaceEnrolled = it.isFaceEnrolled
+            val anyFaceEnrolled = it.isFaceEnabledAndEnrolled
             val anyFingerprintEnrolled = it.isUnlockWithFingerprintPossible(
                     selectedUserInteractor.getSelectedUserId())
             val udfpsEnrolled = it.isUdfpsEnrolled
@@ -372,7 +372,7 @@
         keyguardUpdateMonitor?.let {
             pw.println("   shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" +
                     "${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}")
-            pw.println("   faceEnrolled=${it.isFaceEnrolled}")
+            pw.println("   isFaceEnabledAndEnrolled=${it.isFaceEnabledAndEnrolled}")
             pw.println("   fpUnlockPossible=${
                 it.isUnlockWithFingerprintPossible(selectedUserInteractor.getSelectedUserId())}")
             pw.println("   udfpsEnrolled=${it.isUdfpsEnrolled}")
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 1fa55f5..54cb501 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -384,6 +384,11 @@
         }
     }
 
+    @Nullable
+    View getAodNotifIconContainer() {
+        return mAodIconContainer;
+    }
+
     @Override
     protected void onViewDetached() {
         mClockRegistry.unregisterClockChangeListener(mClockChangedListener);
@@ -639,6 +644,7 @@
                 }
             } else {
                 mNotificationIconAreaController.setupAodIcons(nic);
+                mAodIconContainer = nic;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
deleted file mode 100644
index d7019b5..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard
-
-import android.annotation.CurrentTimeMillisLong
-import com.android.systemui.common.buffer.RingBuffer
-import com.android.systemui.dump.DumpsysTableLogger
-import com.android.systemui.dump.Row
-
-/** Verbose debug information associated. */
-data class KeyguardFaceListenModel(
-    @CurrentTimeMillisLong override var timeMillis: Long = 0L,
-    override var userId: Int = 0,
-    override var listening: Boolean = false,
-    // keep sorted
-    var allowedDisplayStateWhileAwake: Boolean = false,
-    var alternateBouncerShowing: Boolean = false,
-    var authInterruptActive: Boolean = false,
-    var biometricSettingEnabledForUser: Boolean = false,
-    var bouncerFullyShown: Boolean = false,
-    var faceAndFpNotAuthenticated: Boolean = false,
-    var faceAuthAllowed: Boolean = false,
-    var faceDisabled: Boolean = false,
-    var faceLockedOut: Boolean = false,
-    var goingToSleep: Boolean = false,
-    var keyguardAwake: Boolean = false,
-    var keyguardGoingAway: Boolean = false,
-    var listeningForFaceAssistant: Boolean = false,
-    var occludingAppRequestingFaceAuth: Boolean = false,
-    var postureAllowsListening: Boolean = false,
-    var secureCameraLaunched: Boolean = false,
-    var supportsDetect: Boolean = false,
-    var switchingUser: Boolean = false,
-    var systemUser: Boolean = false,
-    var udfpsFingerDown: Boolean = false,
-    var userNotTrustedOrDetectionIsNeeded: Boolean = false,
-) : KeyguardListenModel() {
-
-    /** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */
-    val asStringList: List<String> by lazy {
-        listOf(
-            DATE_FORMAT.format(timeMillis),
-            timeMillis.toString(),
-            userId.toString(),
-            listening.toString(),
-            // keep sorted
-            allowedDisplayStateWhileAwake.toString(),
-            alternateBouncerShowing.toString(),
-            authInterruptActive.toString(),
-            biometricSettingEnabledForUser.toString(),
-            bouncerFullyShown.toString(),
-            faceAndFpNotAuthenticated.toString(),
-            faceAuthAllowed.toString(),
-            faceDisabled.toString(),
-            faceLockedOut.toString(),
-            goingToSleep.toString(),
-            keyguardAwake.toString(),
-            keyguardGoingAway.toString(),
-            listeningForFaceAssistant.toString(),
-            occludingAppRequestingFaceAuth.toString(),
-            postureAllowsListening.toString(),
-            secureCameraLaunched.toString(),
-            supportsDetect.toString(),
-            switchingUser.toString(),
-            systemUser.toString(),
-            udfpsFingerDown.toString(),
-            userNotTrustedOrDetectionIsNeeded.toString(),
-        )
-    }
-
-    /**
-     * [RingBuffer] to store [KeyguardFaceListenModel]. After the buffer is full, it will recycle
-     * old events.
-     *
-     * Do not use [append] to add new elements. Instead use [insert], as it will recycle if
-     * necessary.
-     */
-    class Buffer {
-        private val buffer = RingBuffer(CAPACITY) { KeyguardFaceListenModel() }
-
-        fun insert(model: KeyguardFaceListenModel) {
-            buffer.advance().apply {
-                timeMillis = model.timeMillis
-                userId = model.userId
-                listening = model.listening
-                // keep sorted
-                allowedDisplayStateWhileAwake = model.allowedDisplayStateWhileAwake
-                alternateBouncerShowing = model.alternateBouncerShowing
-                authInterruptActive = model.authInterruptActive
-                biometricSettingEnabledForUser = model.biometricSettingEnabledForUser
-                bouncerFullyShown = model.bouncerFullyShown
-                faceAndFpNotAuthenticated = model.faceAndFpNotAuthenticated
-                faceAuthAllowed = model.faceAuthAllowed
-                faceDisabled = model.faceDisabled
-                faceLockedOut = model.faceLockedOut
-                goingToSleep = model.goingToSleep
-                keyguardAwake = model.keyguardAwake
-                keyguardGoingAway = model.keyguardGoingAway
-                listeningForFaceAssistant = model.listeningForFaceAssistant
-                occludingAppRequestingFaceAuth = model.occludingAppRequestingFaceAuth
-                postureAllowsListening = model.postureAllowsListening
-                secureCameraLaunched = model.secureCameraLaunched
-                supportsDetect = model.supportsDetect
-                switchingUser = model.switchingUser
-                systemUser = model.systemUser
-                udfpsFingerDown = model.udfpsFingerDown
-                userNotTrustedOrDetectionIsNeeded = model.userNotTrustedOrDetectionIsNeeded
-            }
-        }
-        /**
-         * Returns the content of the buffer (sorted from latest to newest).
-         *
-         * @see KeyguardFingerprintListenModel.asStringList
-         */
-        fun toList(): List<Row> {
-            return buffer.asSequence().map { it.asStringList }.toList()
-        }
-    }
-
-    companion object {
-        const val CAPACITY = 40 // number of logs to retain
-
-        /** Headers for dumping a table using [DumpsysTableLogger]. */
-        @JvmField
-        val TABLE_HEADERS =
-            listOf(
-                "timestamp",
-                "time_millis",
-                "userId",
-                "listening",
-                // keep sorted
-                "allowedDisplayStateWhileAwake",
-                "alternateBouncerShowing",
-                "authInterruptActive",
-                "biometricSettingEnabledForUser",
-                "bouncerFullyShown",
-                "faceAndFpNotAuthenticated",
-                "faceAuthAllowed",
-                "faceDisabled",
-                "faceLockedOut",
-                "goingToSleep",
-                "keyguardAwake",
-                "keyguardGoingAway",
-                "listeningForFaceAssistant",
-                "occludingAppRequestingFaceAuth",
-                "postureAllowsListening",
-                "secureCameraLaunched",
-                "supportsDetect",
-                "switchingUser",
-                "systemUser",
-                "udfpsFingerDown",
-                "userNotTrustedOrDetectionIsNeeded",
-            )
-    }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 1b6112f..f706301 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -214,7 +214,6 @@
         public void onUserInput() {
             mBouncerMessageInteractor.onPrimaryBouncerUserInput();
             mKeyguardFaceAuthInteractor.onPrimaryBouncerUserInput();
-            mUpdateMonitor.cancelFaceAuth();
         }
 
         @Override
@@ -340,16 +339,11 @@
     private final SwipeListener mSwipeListener = new SwipeListener() {
         @Override
         public void onSwipeUp() {
-            if (!mUpdateMonitor.isFaceDetectionRunning()) {
-                mKeyguardFaceAuthInteractor.onSwipeUpOnBouncer();
-                boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth(
-                        FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER);
+            if (mKeyguardFaceAuthInteractor.canFaceAuthRun()) {
                 mKeyguardSecurityCallback.userActivity();
-                if (didFaceAuthRun) {
-                    showMessage(/* message= */ null, /* colorState= */ null, /* animated= */ true);
-                }
             }
-            if (mUpdateMonitor.isFaceEnrolled()) {
+            mKeyguardFaceAuthInteractor.onSwipeUpOnBouncer();
+            if (mKeyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) {
                 mUpdateMonitor.requestActiveUnlock(
                         ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
                         "swipeUpOnBouncer");
@@ -755,7 +749,7 @@
         }
         mView.onResume(
                 mSecurityModel.getSecurityMode(mSelectedUserInteractor.getSelectedUserId()),
-                mKeyguardStateController.isFaceEnrolled());
+                mKeyguardStateController.isFaceEnrolledAndEnabled());
     }
 
     /** Sets an initial message that would override the default message */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 87d937b..4fbf077 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -84,6 +84,7 @@
         Dumpable {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     @VisibleForTesting static final String TAG = "KeyguardStatusViewController";
+    private static final long STATUS_AREA_HEIGHT_ANIMATION_MILLIS = 133;
 
     /**
      * Duration to use for the animator when the keyguard status view alignment changes, and a
@@ -104,6 +105,10 @@
     private final KeyguardInteractor mKeyguardInteractor;
     private final PowerInteractor mPowerInteractor;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    private final DozeParameters mDozeParameters;
+
+    private View mStatusArea = null;
+    private ValueAnimator mStatusAreaHeightAnimator = null;
 
     private Boolean mSplitShadeEnabled = false;
     private Boolean mStatusViewCentered = true;
@@ -123,6 +128,46 @@
                 }
             };
 
+    private final View.OnLayoutChangeListener mStatusAreaLayoutChangeListener =
+            new View.OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v,
+                        int left, int top, int right, int bottom,
+                        int oldLeft, int oldTop, int oldRight, int oldBottom
+                ) {
+                    if (!mDozeParameters.getAlwaysOn()) {
+                        return;
+                    }
+
+                    int oldHeight = oldBottom - oldTop;
+                    int diff = v.getHeight() - oldHeight;
+                    if (diff == 0) {
+                        return;
+                    }
+
+                    int startValue = -1 * diff;
+                    long duration = STATUS_AREA_HEIGHT_ANIMATION_MILLIS;
+                    if (mStatusAreaHeightAnimator != null
+                            && mStatusAreaHeightAnimator.isRunning()) {
+                        duration += mStatusAreaHeightAnimator.getDuration()
+                                - mStatusAreaHeightAnimator.getCurrentPlayTime();
+                        startValue += (int) mStatusAreaHeightAnimator.getAnimatedValue();
+                        mStatusAreaHeightAnimator.cancel();
+                        mStatusAreaHeightAnimator = null;
+                    }
+
+                    mStatusAreaHeightAnimator = ValueAnimator.ofInt(startValue, 0);
+                    mStatusAreaHeightAnimator.setDuration(duration);
+                    final View nic = mKeyguardClockSwitchController.getAodNotifIconContainer();
+                    if (nic != null) {
+                        mStatusAreaHeightAnimator.addUpdateListener(anim -> {
+                            nic.setTranslationY((int) anim.getAnimatedValue());
+                        });
+                    }
+                    mStatusAreaHeightAnimator.start();
+                }
+            };
+
     @Inject
     public KeyguardStatusViewController(
             KeyguardStatusView keyguardStatusView,
@@ -144,6 +189,7 @@
         mKeyguardClockSwitchController = keyguardClockSwitchController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mConfigurationController = configurationController;
+        mDozeParameters = dozeParameters;
         mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController,
                 dozeParameters, screenOffAnimationController, /* animateYPos= */ true,
                 logger.getBuffer());
@@ -218,12 +264,15 @@
 
     @Override
     protected void onViewAttached() {
+        mStatusArea = mView.findViewById(R.id.keyguard_status_area);
+        mStatusArea.addOnLayoutChangeListener(mStatusAreaLayoutChangeListener);
         mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
         mConfigurationController.addCallback(mConfigurationListener);
     }
 
     @Override
     protected void onViewDetached() {
+        mStatusArea.removeOnLayoutChangeListener(mStatusAreaLayoutChangeListener);
         mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
         mConfigurationController.removeCallback(mConfigurationListener);
     }
@@ -293,9 +342,15 @@
     /**
      * Get the height of the keyguard status view without the notification icon area, as that's
      * only visible on AOD.
+     *
+     * We internally animate height changes to the status area to prevent discontinuities in the
+     * doze animation introduced by the height suddenly changing due to smartpace.
      */
     public int getLockscreenHeight() {
-        return mView.getHeight() - mKeyguardClockSwitchController.getNotificationIconAreaHeight();
+        int heightAnimValue = mStatusAreaHeightAnimator == null ? 0 :
+                (int) mStatusAreaHeightAnimator.getAnimatedValue();
+        return mView.getHeight() + heightAnimValue
+                - mKeyguardClockSwitchController.getNotificationIconAreaHeight();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index baab637..c5bb099 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -34,48 +34,11 @@
 import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
 import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT;
-import static android.os.PowerManager.WAKE_REASON_UNKNOWN;
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
-import static com.android.keyguard.FaceAuthReasonKt.apiRequestReasonToUiEvent;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_DISPLAY_OFF;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_DREAM_STARTED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FINISHED_GOING_TO_SLEEP;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FP_LOCKED_OUT;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_KEYGUARD_GOING_AWAY;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_TRUST_ENABLED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_USER_INPUT_ON_BOUNCER;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALL_AUTHENTICATORS_REGISTERED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_DURING_CANCELLATION;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ENROLLMENTS_CHANGED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_FACE_LOCKOUT_RESET;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ON_REACH_GESTURE_ON_AOD;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_TRUST_DISABLED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_CAMERA_LAUNCHED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_FP_AUTHENTICATED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_GOING_TO_SLEEP;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_OCCLUSION_CHANGED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_RESET;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_KEYGUARD_INIT;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_POSTURE_CHANGED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
-import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
 
@@ -104,10 +67,7 @@
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
 import android.hardware.biometrics.SensorProperties;
 import android.hardware.biometrics.SensorPropertiesInternal;
-import android.hardware.face.FaceAuthenticateOptions;
 import android.hardware.face.FaceManager;
-import android.hardware.face.FaceSensorPropertiesInternal;
-import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
 import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
@@ -137,7 +97,6 @@
 import android.text.TextUtils;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
-import android.view.Display;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -161,7 +120,6 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.DumpsysTableLogger;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
 import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
@@ -172,12 +130,10 @@
 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.keyguard.shared.model.SysUiFaceAuthenticateOptions;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.WeatherData;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
-import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -318,7 +274,6 @@
     private final boolean mIsSystemUser;
     private final AuthController mAuthController;
     private final UiEventLogger mUiEventLogger;
-    private final Set<Integer> mFaceAcquiredInfoIgnoreList;
     private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
     private final PackageManager mPackageManager;
     private int mStatusBarState;
@@ -339,26 +294,6 @@
             }
         }
     };
-    private final DisplayTracker.Callback mDisplayCallback = new DisplayTracker.Callback() {
-        @Override
-        public void onDisplayChanged(int displayId) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                return;
-            }
-
-            if (mWakefulness.getWakefulness() == WAKEFULNESS_AWAKE
-                    && mDisplayTracker.getDisplay(mDisplayTracker.getDefaultDisplayId()).getState()
-                    == Display.STATE_OFF) {
-                mAllowedDisplayStateWhileAwakeForFaceAuth = false;
-                updateFaceListeningState(
-                        BIOMETRIC_ACTION_STOP,
-                        FACE_AUTH_DISPLAY_OFF
-                );
-            } else {
-                mAllowedDisplayStateWhileAwakeForFaceAuth = true;
-            }
-        }
-    };
     private final FaceWakeUpTriggersConfig mFaceWakeUpTriggersConfig;
 
     HashMap<Integer, SimData> mSimDatas = new HashMap<>();
@@ -377,9 +312,7 @@
     private boolean mNeedsSlowUnlockTransition;
     private boolean mAssistantVisible;
     private boolean mOccludingAppRequestingFp;
-    private boolean mOccludingAppRequestingFace;
     private boolean mSecureCameraLaunched;
-    private boolean mAllowedDisplayStateWhileAwakeForFaceAuth = true;
     private boolean mBiometricPromptShowing;
     @VisibleForTesting
     protected boolean mTelephonyCapable;
@@ -409,7 +342,6 @@
     private final TrustManager mTrustManager;
     private final UserManager mUserManager;
     private final DevicePolicyManager mDevicePolicyManager;
-    private final DevicePostureController mPostureController;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final InteractionJankMonitor mInteractionJankMonitor;
     private final LatencyTracker mLatencyTracker;
@@ -422,13 +354,9 @@
     @Nullable
     private final FingerprintManager mFpm;
     @Nullable
-    private final FaceManager mFaceManager;
-    @Nullable
     private KeyguardFaceAuthInteractor mFaceAuthInteractor;
     private final TaskStackChangeListeners mTaskStackChangeListeners;
     private final IActivityTaskManager mActivityTaskManager;
-    private final WakefulnessLifecycle mWakefulness;
-    private final DisplayTracker mDisplayTracker;
     private final SelectedUserInteractor mSelectedUserInteractor;
     private final LockPatternUtils mLockPatternUtils;
     @VisibleForTesting
@@ -439,11 +367,9 @@
     private List<SubscriptionInfo> mSubscriptionInfo;
     @VisibleForTesting
     protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
-    private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
     private boolean mIsDreaming;
     private boolean mLogoutEnabled;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-    private int mPostureState = DEVICE_POSTURE_UNKNOWN;
     private FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
 
     /**
@@ -455,7 +381,6 @@
 
     // If the HAL dies or is unable to authenticate, keyguard should retry after a short delay
     private int mHardwareFingerprintUnavailableRetryCount = 0;
-    private int mHardwareFaceUnavailableRetryCount = 0;
     private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms
     private static final int HAL_ERROR_RETRY_MAX = 20;
 
@@ -465,7 +390,6 @@
     @VisibleForTesting
     protected final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
 
-    private final Runnable mFaceCancelNotReceived = this::onFaceCancelNotReceived;
     private final Provider<SessionTracker> mSessionTrackerProvider;
 
     @VisibleForTesting
@@ -481,8 +405,7 @@
                 public void onChanged(boolean enabled, int userId) {
                     mHandler.post(() -> {
                         mBiometricEnabledForUser.put(userId, enabled);
-                        updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                                FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD);
+                        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
                     });
                 }
             };
@@ -525,15 +448,11 @@
 
     private final KeyguardFingerprintListenModel.Buffer mFingerprintListenBuffer =
             new KeyguardFingerprintListenModel.Buffer();
-    private final KeyguardFaceListenModel.Buffer mFaceListenBuffer =
-            new KeyguardFaceListenModel.Buffer();
     private final KeyguardActiveUnlockModel.Buffer mActiveUnlockTriggerBuffer =
             new KeyguardActiveUnlockModel.Buffer();
 
     @VisibleForTesting
     SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>();
-    @VisibleForTesting
-    SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>();
 
     private static int sCurrentUser;
 
@@ -561,11 +480,9 @@
         // authenticating.  TrustManager sends an onTrustChanged whenever a user unlocks keyguard,
         // for this reason we need to make sure to not authenticate.
         if (wasTrusted == enabled || enabled) {
-            updateBiometricListeningState(BIOMETRIC_ACTION_STOP,
-                    FACE_AUTH_STOPPED_TRUST_ENABLED);
+            updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
         } else {
-            updateBiometricListeningState(BIOMETRIC_ACTION_START,
-                    FACE_AUTH_TRIGGERED_TRUST_DISABLED);
+            updateFingerprintListeningState(BIOMETRIC_ACTION_START);
         }
 
         mLogger.logTrustChanged(wasTrusted, enabled, userId);
@@ -807,8 +724,6 @@
     public void setKeyguardGoingAway(boolean goingAway) {
         mKeyguardGoingAway = goingAway;
         if (mKeyguardGoingAway) {
-            updateFaceListeningState(BIOMETRIC_ACTION_STOP,
-                    FACE_AUTH_STOPPED_KEYGUARD_GOING_AWAY);
             for (int i = 0; i < mCallbacks.size(); i++) {
                 KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
                 if (cb != null) {
@@ -855,29 +770,12 @@
             }
         }
 
-        if (occlusionChanged) {
-            updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                    FACE_AUTH_UPDATED_KEYGUARD_OCCLUSION_CHANGED);
-        } else if (showingChanged) {
-            updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                    FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED);
+        if (occlusionChanged || showingChanged) {
+            updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
         }
     }
 
     /**
-     * Request to listen for face authentication when an app is occluding keyguard.
-     *
-     * @param request if true and mKeyguardOccluded, request face auth listening, else default
-     *                to normal behavior.
-     *                See {@link KeyguardUpdateMonitor#shouldListenForFace()}
-     */
-    public void requestFaceAuthOnOccludingApp(boolean request) {
-        mOccludingAppRequestingFace = request;
-        int action = mOccludingAppRequestingFace ? BIOMETRIC_ACTION_UPDATE : BIOMETRIC_ACTION_STOP;
-        updateFaceListeningState(action, FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED);
-    }
-
-    /**
      * Request to listen for fingerprint when an app is occluding keyguard.
      *
      * @param request if true and mKeyguardOccluded, request fingerprint listening, else default
@@ -894,8 +792,7 @@
      */
     public void onCameraLaunched() {
         mSecureCameraLaunched = true;
-        updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                FACE_AUTH_UPDATED_CAMERA_LAUNCHED);
+        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
     }
 
     /**
@@ -951,8 +848,7 @@
         // Don't send cancel if authentication succeeds
         mFingerprintCancelSignal = null;
         mLogger.logFingerprintSuccess(userId, isStrongBiometric);
-        updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                FACE_AUTH_UPDATED_FP_AUTHENTICATED);
+        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1025,7 +921,6 @@
             mLogger.logFingerprintDetected(authUserId, isStrongBiometric);
         } else if (biometricSourceType == FACE) {
             mLogger.logFaceDetected(authUserId, isStrongBiometric);
-            setFaceRunningState(BIOMETRIC_STATE_STOPPED);
         }
 
         Trace.endSection();
@@ -1140,7 +1035,6 @@
             if (isUdfpsEnrolled()) {
                 updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
             }
-            stopListeningForFace(FACE_AUTH_STOPPED_FP_LOCKED_OUT);
         }
 
         mLogger.logFingerprintError(msgId, errString);
@@ -1218,16 +1112,11 @@
     public void onFaceAuthenticated(int userId, boolean isStrongBiometric) {
         Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
         Assert.isMainThread();
-        mUserFaceAuthenticated.put(userId,
-                new BiometricAuthenticated(true, isStrongBiometric));
         // Update/refresh trust state only if user can skip bouncer
         if (getUserCanSkipBouncer(userId)) {
             mTrustManager.unlockedByBiometricForUser(userId, FACE);
         }
-        // Don't send cancel if authentication succeeds
-        mFaceCancelSignal = null;
-        updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED);
+        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
         mLogger.d("onFaceAuthenticated");
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1247,11 +1136,6 @@
         Trace.endSection();
     }
 
-    /**
-     * @deprecated This is being migrated to use modern architecture, this method is visible purely
-     * for bridging the gap while the migration is active.
-     */
-    @Deprecated
     private void handleFaceAuthFailed() {
         Assert.isMainThread();
         String reason =
@@ -1264,8 +1148,6 @@
                 "faceFailure-" + reason);
 
         mLogger.d("onFaceAuthFailed");
-        mFaceCancelSignal = null;
-        setFaceRunningState(BIOMETRIC_STATE_STOPPED);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1276,11 +1158,6 @@
                 mContext.getString(R.string.kg_face_not_recognized));
     }
 
-    /**
-     * @deprecated This is being migrated to use modern architecture, this method is visible purely
-     * for bridging the gap while the migration is active.
-     */
-    @Deprecated
     private void handleFaceAcquired(int acquireInfo) {
         Assert.isMainThread();
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1298,44 +1175,23 @@
         }
     }
 
-    /**
-     * @deprecated This is being migrated to use modern architecture, this method is visible purely
-     * for bridging the gap while the migration is active.
-     */
-    @Deprecated
     private void handleFaceAuthenticated(int authUserId, boolean isStrongBiometric) {
         Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated");
-        try {
-            if (mGoingToSleep) {
-                mLogger.d("Aborted successful auth because device is going to sleep.");
-                return;
-            }
-            final int userId = mSelectedUserInteractor.getSelectedUserId(true);
-            if (userId != authUserId) {
-                mLogger.logFaceAuthForWrongUser(authUserId);
-                return;
-            }
-            if (!isFaceAuthInteractorEnabled() && isFaceDisabled(userId)) {
-                mLogger.logFaceAuthDisabledForUser(userId);
-                return;
-            }
-            mLogger.logFaceAuthSuccess(userId);
-            onFaceAuthenticated(userId, isStrongBiometric);
-        } finally {
-            setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+        if (mGoingToSleep) {
+            mLogger.d("Aborted successful auth because device is going to sleep.");
+            return;
         }
+        final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+        if (userId != authUserId) {
+            mLogger.logFaceAuthForWrongUser(authUserId);
+            return;
+        }
+        mLogger.logFaceAuthSuccess(userId);
+        onFaceAuthenticated(userId, isStrongBiometric);
         Trace.endSection();
     }
 
-    /**
-     * @deprecated This is being migrated to use modern architecture, this method is visible purely
-     * for bridging the gap while the migration is active.
-     */
-    @Deprecated
     private void handleFaceHelp(int msgId, String helpString) {
-        if (mFaceAcquiredInfoIgnoreList.contains(msgId)) {
-            return;
-        }
         Assert.isMainThread();
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1345,49 +1201,19 @@
         }
     }
 
-    /**
-     * @deprecated This is being migrated to use modern architecture, this method is visible purely
-     * for bridging the gap while the migration is active.
-     */
-    @Deprecated
     private void handleFaceError(int msgId, final String originalErrMsg) {
         Assert.isMainThread();
         String errString = originalErrMsg;
         mLogger.logFaceAuthError(msgId, originalErrMsg);
-        if (mHandler.hasCallbacks(mFaceCancelNotReceived)) {
-            mHandler.removeCallbacks(mFaceCancelNotReceived);
-        }
 
         // Error is always the end of authentication lifecycle
-        mFaceCancelSignal = null;
         boolean cameraPrivacyEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled(
                 SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, SensorPrivacyManager.Sensors.CAMERA);
 
-        if (msgId == FaceManager.FACE_ERROR_CANCELED
-                && mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
-            setFaceRunningState(BIOMETRIC_STATE_STOPPED);
-            updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
-                    FACE_AUTH_TRIGGERED_DURING_CANCELLATION);
-        } else {
-            setFaceRunningState(BIOMETRIC_STATE_STOPPED);
-        }
-
         final boolean isHwUnavailable = msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE;
 
-        if (isHwUnavailable
-                || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS) {
-            if (mHardwareFaceUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
-                mHardwareFaceUnavailableRetryCount++;
-                mHandler.removeCallbacks(mRetryFaceAuthentication);
-                mHandler.postDelayed(mRetryFaceAuthentication, HAL_ERROR_RETRY_TIMEOUT);
-            }
-        }
-
-        boolean lockedOutStateChanged = false;
         if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
-            lockedOutStateChanged = !mFaceLockedOutPermanent;
-            mFaceLockedOutPermanent = true;
-            if (isFaceClass3()) {
+            if (getFaceAuthInteractor() != null && getFaceAuthInteractor().isFaceAuthStrong()) {
                 updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
             }
         }
@@ -1404,10 +1230,6 @@
             }
         }
 
-        if (lockedOutStateChanged) {
-            notifyLockedOutStateChanged(FACE);
-        }
-
         if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(msgId)) {
             requestActiveUnlock(
                     ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
@@ -1415,49 +1237,6 @@
         }
     }
 
-    private final Runnable mRetryFaceAuthentication = new Runnable() {
-        @Override
-        public void run() {
-            mLogger.logRetryingAfterFaceHwUnavailable(mHardwareFaceUnavailableRetryCount);
-            updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
-                    FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE);
-        }
-    };
-
-    private void onFaceCancelNotReceived() {
-        mLogger.e("Face cancellation not received, transitioning to STOPPED");
-        mFaceRunningState = BIOMETRIC_STATE_STOPPED;
-        KeyguardUpdateMonitor.this.updateFaceListeningState(BIOMETRIC_ACTION_STOP,
-                FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED);
-    }
-
-    private void handleFaceLockoutReset(@LockoutMode int mode) {
-        mLogger.logFaceLockoutReset(mode);
-        final boolean wasLockoutPermanent = mFaceLockedOutPermanent;
-        mFaceLockedOutPermanent = (mode == BIOMETRIC_LOCKOUT_PERMANENT);
-        final boolean changed = (mFaceLockedOutPermanent != wasLockoutPermanent);
-
-        mHandler.postDelayed(() -> updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
-                FACE_AUTH_TRIGGERED_FACE_LOCKOUT_RESET), getBiometricLockoutDelay());
-
-        if (changed) {
-            notifyLockedOutStateChanged(FACE);
-        }
-    }
-
-    private void setFaceRunningState(int faceRunningState) {
-        boolean wasRunning = mFaceRunningState == BIOMETRIC_STATE_RUNNING;
-        boolean isRunning = faceRunningState == BIOMETRIC_STATE_RUNNING;
-        mFaceRunningState = faceRunningState;
-        mLogger.logFaceRunningState(mFaceRunningState);
-        // Clients of KeyguardUpdateMonitor don't care about the internal state or about the
-        // asynchronousness of the cancel cycle. So only notify them if the actually running state
-        // has changed.
-        if (wasRunning != isRunning) {
-            notifyFaceRunningStateChanged();
-        }
-    }
-
     private void notifyFaceRunningStateChanged() {
         Assert.isMainThread();
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1478,14 +1257,7 @@
      */
     @Deprecated
     public boolean isFaceDetectionRunning() {
-        if (isFaceAuthInteractorEnabled()) {
-            return getFaceAuthInteractor().isRunning();
-        }
-        return mFaceRunningState == BIOMETRIC_STATE_RUNNING;
-    }
-
-    private boolean isFaceAuthInteractorEnabled() {
-        return mFaceAuthInteractor != null && mFaceAuthInteractor.isEnabled();
+        return getFaceAuthInteractor() != null && getFaceAuthInteractor().isRunning();
     }
 
     private @Nullable KeyguardFaceAuthInteractor getFaceAuthInteractor() {
@@ -1495,14 +1267,32 @@
     /**
      * Set the face auth interactor that should be used for initiating face authentication.
      */
-    public void setFaceAuthInteractor(@Nullable KeyguardFaceAuthInteractor faceAuthInteractor) {
+    public void setFaceAuthInteractor(KeyguardFaceAuthInteractor faceAuthInteractor) {
+        if (mFaceAuthInteractor != null) {
+            mFaceAuthInteractor.unregisterListener(mFaceAuthenticationListener);
+        }
         mFaceAuthInteractor = faceAuthInteractor;
         mFaceAuthInteractor.registerListener(mFaceAuthenticationListener);
     }
 
-    private FaceAuthenticationListener mFaceAuthenticationListener =
+    private final FaceAuthenticationListener mFaceAuthenticationListener =
             new FaceAuthenticationListener() {
                 @Override
+                public void onAuthEnrollmentStateChanged(boolean enrolled) {
+                    notifyAboutEnrollmentChange(TYPE_FACE);
+                }
+
+                @Override
+                public void onRunningStateChanged(boolean isRunning) {
+                    notifyFaceRunningStateChanged();
+                }
+
+                @Override
+                public void onLockoutStateChanged(boolean isLockedOut) {
+                    notifyLockedOutStateChanged(FACE);
+                }
+
+                @Override
                 public void onAuthenticationStatusChanged(
                         @NonNull FaceAuthenticationStatus status
                 ) {
@@ -1546,32 +1336,14 @@
     }
 
     /**
-     * @deprecated This method is not needed anymore with the new face auth system.
-     */
-    @Deprecated
-    private boolean isFaceDisabled(int userId) {
-        // TODO(b/140035044)
-        return whitelistIpcs(() ->
-                (mDevicePolicyManager.getKeyguardDisabledFeatures(null, userId)
-                        & DevicePolicyManager.KEYGUARD_DISABLE_FACE) != 0
-                || isSimPinSecure());
-    }
-
-    /**
      * @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 This is being migrated to use modern architecture.
+     * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()}
      */
     @Deprecated
     public boolean getIsFaceAuthenticated() {
-        boolean faceAuthenticated = false;
-        BiometricAuthenticated bioFaceAuthenticated =
-                mUserFaceAuthenticated.get(mSelectedUserInteractor.getSelectedUserId());
-        if (bioFaceAuthenticated != null) {
-            faceAuthenticated = bioFaceAuthenticated.mAuthenticated;
-        }
-        return faceAuthenticated;
+        return getFaceAuthInteractor() != null && getFaceAuthInteractor().isAuthenticated();
     }
 
     public boolean getUserCanSkipBouncer(int userId) {
@@ -1590,17 +1362,19 @@
         BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
         boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated
                 && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric);
-        return fingerprintAllowed || getUserUnlockedWithFace(userId);
+        boolean unlockedByFace = isCurrentUserUnlockedWithFace() && isUnlockingWithBiometricAllowed(
+                FACE);
+        return fingerprintAllowed || unlockedByFace;
     }
 
 
     /**
      * Returns whether the user is unlocked with face.
+     * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()} instead
      */
-    public boolean getUserUnlockedWithFace(int userId) {
-        BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
-        return face != null && face.mAuthenticated
-                && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric);
+    @Deprecated
+    public boolean isCurrentUserUnlockedWithFace() {
+        return getFaceAuthInteractor() != null && getFaceAuthInteractor().isAuthenticated();
     }
 
     /**
@@ -1609,13 +1383,11 @@
      */
     public boolean getUserUnlockedWithBiometricAndIsBypassing(int userId) {
         BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
-        BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
         // fingerprint always bypasses
         boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated
                 && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric);
-        boolean faceAllowed = face != null && face.mAuthenticated
-                && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric);
-        return fingerprintAllowed || faceAllowed && mKeyguardBypassController.canBypass();
+        return fingerprintAllowed || (isCurrentUserUnlockedWithFace()
+                && mKeyguardBypassController.canBypass());
     }
 
     public boolean getUserTrustIsManaged(int userId) {
@@ -1684,10 +1456,22 @@
         // STRONG_AUTH_REQUIRED_AFTER_LOCKOUT which is the same as mFingerprintLockedOutPermanent;
         // however the strong auth tracker does not include the temporary lockout
         // mFingerprintLockedOut.
+        if (!mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric)) {
+            return false;
+        }
+        boolean isFaceLockedOut =
+                getFaceAuthInteractor() != null && getFaceAuthInteractor().isLockedOut();
+        boolean isFaceAuthStrong =
+                getFaceAuthInteractor() != null && getFaceAuthInteractor().isFaceAuthStrong();
+        boolean isFingerprintLockedOut = isFingerprintLockedOut();
+        boolean isAnyStrongBiometricLockedOut =
+                (isFingerprintClass3() && isFingerprintLockedOut) || (isFaceAuthStrong
+                        && isFaceLockedOut);
         // Class 3 biometric lockout will lockout ALL biometrics
-        return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric)
-                && (!isFingerprintClass3() || !isFingerprintLockedOut())
-                && (!isFaceClass3() || !mFaceLockedOutPermanent);
+        if (isAnyStrongBiometricLockedOut) {
+            return false;
+        }
+        return !isFaceLockedOut || !isFingerprintLockedOut;
     }
 
     /**
@@ -1707,7 +1491,9 @@
             case FINGERPRINT:
                 return isUnlockingWithBiometricAllowed(isFingerprintClass3());
             case FACE:
-                return isUnlockingWithBiometricAllowed(isFaceClass3());
+                return getFaceAuthInteractor() != null
+                        && isUnlockingWithBiometricAllowed(
+                        getFaceAuthInteractor().isFaceAuthStrong());
             default:
                 return false;
         }
@@ -1757,14 +1543,9 @@
             }
         }
         if (userId == mSelectedUserInteractor.getSelectedUserId()) {
-            FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED.setExtraInfo(
-                    mStrongAuthTracker.getStrongAuthForUser(
-                            mSelectedUserInteractor.getSelectedUserId()));
-
             // Strong auth is only reset when primary auth is used to enter the device,
             // so we only check whether to stop biometric listening states here
-            updateBiometricListeningState(
-                    BIOMETRIC_ACTION_STOP, FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED);
+            updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
         }
     }
 
@@ -1787,14 +1568,9 @@
             }
         }
         if (userId == mSelectedUserInteractor.getSelectedUserId()) {
-            FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED.setExtraInfo(
-                    mStrongAuthTracker.isNonStrongBiometricAllowedAfterIdleTimeout(
-                            mSelectedUserInteractor.getSelectedUserId()) ? -1 : 1);
-
             // This is only reset when primary auth is used to enter the device, so we only check
             // whether to stop biometric listening states here
-            updateBiometricListeningState(BIOMETRIC_ACTION_STOP,
-                    FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED);
+            updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
         }
     }
 
@@ -1813,11 +1589,10 @@
     void setAssistantVisible(boolean assistantVisible) {
         mAssistantVisible = assistantVisible;
         mLogger.logAssistantVisible(mAssistantVisible);
-        if (isFaceAuthInteractorEnabled()) {
-            mFaceAuthInteractor.onAssistantTriggeredOnLockScreen();
+        if (getFaceAuthInteractor() != null) {
+            getFaceAuthInteractor().onAssistantTriggeredOnLockScreen();
         }
-        updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED);
+        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
         if (mAssistantVisible) {
             requestActiveUnlock(
                     ActiveUnlockConfig.ActiveUnlockRequestOrigin.ASSISTANT,
@@ -1929,14 +1704,6 @@
         }
     };
 
-    private final FaceManager.LockoutResetCallback mFaceLockoutResetCallback
-            = new FaceManager.LockoutResetCallback() {
-        @Override
-        public void onLockoutReset(int sensorId) {
-            handleFaceLockoutReset(BIOMETRIC_LOCKOUT_NONE);
-        }
-    };
-
     /**
      * Propagates a pointer down event to keyguard.
      */
@@ -1998,7 +1765,6 @@
                 @Override
                 public void onUdfpsPointerDown(int sensorId) {
                     mLogger.logUdfpsPointerDown(sensorId);
-                    requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
                 }
 
                 /**
@@ -2024,56 +1790,12 @@
                 }
             };
 
-    private final FaceManager.FaceDetectionCallback mFaceDetectionCallback
-            = (sensorId, userId, isStrongBiometric) -> {
-                // Trigger the face detected path so the bouncer can be shown
-                handleBiometricDetected(userId, FACE, isStrongBiometric);
-            };
-
-    @VisibleForTesting
-    final FaceManager.AuthenticationCallback mFaceAuthenticationCallback
-            = new FaceManager.AuthenticationCallback() {
-
-                @Override
-                public void onAuthenticationFailed() {
-                    handleFaceAuthFailed();
-                }
-
-                @Override
-                public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) {
-                    handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric());
-                }
-
-                @Override
-                public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
-                    handleFaceHelp(helpMsgId, helpString.toString());
-                }
-
-                @Override
-                public void onAuthenticationError(int errMsgId, CharSequence errString) {
-                    handleFaceError(errMsgId, errString.toString());
-                }
-
-                @Override
-                public void onAuthenticationAcquired(int acquireInfo) {
-                    handleFaceAcquired(acquireInfo);
-                }
-    };
-
     @VisibleForTesting
     final DevicePostureController.Callback mPostureCallback =
             new DevicePostureController.Callback() {
                 @Override
                 public void onPostureChanged(@DevicePostureInt int posture) {
-                    boolean currentPostureAllowsFaceAuth = doesPostureAllowFaceAuth(mPostureState);
-                    boolean newPostureAllowsFaceAuth = doesPostureAllowFaceAuth(posture);
-                    mPostureState = posture;
-                    if (currentPostureAllowsFaceAuth && !newPostureAllowsFaceAuth) {
-                        mLogger.d("New posture does not allow face auth, stopping it");
-                        updateFaceListeningState(BIOMETRIC_ACTION_STOP,
-                                FACE_AUTH_UPDATED_POSTURE_CHANGED);
-                    }
-                    if (mPostureState == DEVICE_POSTURE_OPENED) {
+                    if (posture == DEVICE_POSTURE_OPENED) {
                         mLogger.d("Posture changed to open - attempting to request active unlock");
                         requestActiveUnlockFromWakeReason(PowerManager.WAKE_REASON_UNFOLD_DEVICE,
                                 false);
@@ -2083,14 +1805,10 @@
 
     @VisibleForTesting
     CancellationSignal mFingerprintCancelSignal;
-    @VisibleForTesting
-    CancellationSignal mFaceCancelSignal;
     private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties =
             Collections.emptyList();
-    private List<FaceSensorPropertiesInternal> mFaceSensorProperties = Collections.emptyList();
     private boolean mFingerprintLockedOut;
     private boolean mFingerprintLockedOutPermanent;
-    private boolean mFaceLockedOutPermanent;
 
     /**
      * When we receive a {@link android.content.Intent#ACTION_SIM_STATE_CHANGED} broadcast,
@@ -2229,15 +1947,7 @@
         Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
         Assert.isMainThread();
 
-        mAllowedDisplayStateWhileAwakeForFaceAuth = true;
         updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
-        if (mFaceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(pmWakeReason)) {
-            FACE_AUTH_UPDATED_STARTED_WAKING_UP.setExtraInfo(pmWakeReason);
-            updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
-                    FACE_AUTH_UPDATED_STARTED_WAKING_UP);
-        } else {
-            mLogger.logSkipUpdateFaceListeningOnWakeup(pmWakeReason);
-        }
         requestActiveUnlockFromWakeReason(pmWakeReason, true);
 
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -2264,7 +1974,7 @@
         // which results in face auth running once on AoD.
         mAssistantVisible = false;
         mLogger.d("Started going to sleep, mGoingToSleep=true, mAssistantVisible=false");
-        updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_GOING_TO_SLEEP);
+        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
     }
 
     protected void handleFinishedGoingToSleep(int arg1) {
@@ -2276,15 +1986,12 @@
                 cb.onFinishedGoingToSleep(arg1);
             }
         }
-        updateFaceListeningState(BIOMETRIC_ACTION_STOP,
-                FACE_AUTH_STOPPED_FINISHED_GOING_TO_SLEEP);
         updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
     }
 
     private void handleScreenTurnedOff() {
         Assert.isMainThread();
         mHardwareFingerprintUnavailableRetryCount = 0;
-        mHardwareFaceUnavailableRetryCount = 0;
     }
 
     private void handleDreamingStateChanged(int dreamStart) {
@@ -2297,9 +2004,6 @@
             }
         }
         updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
-        if (mIsDreaming) {
-            updateFaceListeningState(BIOMETRIC_ACTION_STOP, FACE_AUTH_STOPPED_DREAM_STARTED);
-        }
     }
 
     private void handleUserUnlocked(int userId) {
@@ -2344,7 +2048,6 @@
     @VisibleForTesting
     void resetBiometricListeningState() {
         mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
-        mFaceRunningState = BIOMETRIC_STATE_STOPPED;
     }
 
     @VisibleForTesting
@@ -2376,17 +2079,14 @@
             SensorPrivacyManager sensorPrivacyManager,
             TelephonyManager telephonyManager,
             PackageManager packageManager,
-            @Nullable FaceManager faceManager,
             @Nullable FingerprintManager fingerprintManager,
             @Nullable BiometricManager biometricManager,
             FaceWakeUpTriggersConfig faceWakeUpTriggersConfig,
             DevicePostureController devicePostureController,
             Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider,
             TaskStackChangeListeners taskStackChangeListeners,
-            IActivityTaskManager activityTaskManagerService,
-            DisplayTracker displayTracker,
-            WakefulnessLifecycle wakefulness,
-            SelectedUserInteractor selectedUserInteractor) {
+            SelectedUserInteractor selectedUserInteractor,
+            IActivityTaskManager activityTaskManagerService) {
         mContext = context;
         mSubscriptionManager = subscriptionManager;
         mUserTracker = userTracker;
@@ -2413,16 +2113,9 @@
         mDreamManager = dreamManager;
         mTelephonyManager = telephonyManager;
         mDevicePolicyManager = devicePolicyManager;
-        mPostureController = devicePostureController;
         mPackageManager = packageManager;
         mFpm = fingerprintManager;
-        mFaceManager = faceManager;
         mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
-        mFaceAcquiredInfoIgnoreList = Arrays.stream(
-                mContext.getResources().getIntArray(
-                        R.array.config_face_acquire_device_entry_ignorelist))
-                .boxed()
-                .collect(Collectors.toSet());
         mConfigFaceAuthSupportedPosture = mContext.getResources().getInteger(
                 R.integer.config_face_auth_supported_posture);
         mFaceWakeUpTriggersConfig = faceWakeUpTriggersConfig;
@@ -2432,9 +2125,6 @@
                 .collect(Collectors.toSet());
         mTaskStackChangeListeners = taskStackChangeListeners;
         mActivityTaskManager = activityTaskManagerService;
-        mWakefulness = wakefulness;
-        mDisplayTracker = displayTracker;
-        mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
         mSelectedUserInteractor = selectedUserInteractor;
 
         mHandler = new Handler(mainLooper) {
@@ -2521,8 +2211,7 @@
                         setAssistantVisible((boolean) msg.obj);
                         break;
                     case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE:
-                        updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                                FACE_AUTH_UPDATED_FP_AUTHENTICATED);
+                        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
                         break;
                     case MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED:
                         updateLogoutEnabled();
@@ -2617,18 +2306,6 @@
                     });
             mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
         }
-        if (mFaceManager != null) {
-            mFaceManager.addAuthenticatorsRegisteredCallback(
-                    new IFaceAuthenticatorsRegisteredCallback.Stub() {
-                        @Override
-                        public void onAllAuthenticatorsRegistered(
-                                List<FaceSensorPropertiesInternal> sensors) throws RemoteException {
-                            mFaceSensorProperties = sensors;
-                            mLogger.d("FaceManager onAllAuthenticatorsRegistered");
-                        }
-                    });
-            mFaceManager.addLockoutResetCallback(mFaceLockoutResetCallback);
-        }
 
         if (biometricManager != null) {
             biometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback);
@@ -2639,16 +2316,16 @@
             @Override
             public void onAllAuthenticatorsRegistered(
                     @BiometricAuthenticator.Modality int modality) {
-                mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                        FACE_AUTH_TRIGGERED_ALL_AUTHENTICATORS_REGISTERED));
+                mainExecutor.execute(
+                        () -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE));
             }
 
             @Override
             public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
                 mHandler.obtainMessage(MSG_BIOMETRIC_ENROLLMENT_STATE_CHANGED, modality, 0)
                         .sendToTarget();
-                mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                        FACE_AUTH_TRIGGERED_ENROLLMENTS_CHANGED));
+                mainExecutor.execute(
+                        () -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE));
             }
 
             @Override
@@ -2665,9 +2342,9 @@
             }
         });
         if (mConfigFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) {
-            mPostureController.addCallback(mPostureCallback);
+            devicePostureController.addCallback(mPostureCallback);
         }
-        updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_ON_KEYGUARD_INIT);
+        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
 
         mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
         mIsSystemUser = mUserManager.isSystemUser();
@@ -2721,10 +2398,6 @@
         }
     }
 
-    private boolean isFaceSupported() {
-        return mFaceManager != null && !mFaceSensorProperties.isEmpty();
-    }
-
     private boolean isFingerprintSupported() {
         return mFpm != null && !mFingerprintSensorProperties.isEmpty();
     }
@@ -2760,17 +2433,13 @@
     }
 
     /**
-     * @return true if there's at least one face enrolled for the given user.
-     */
-    public boolean isFaceEnrolled(int userId) {
-        return mAuthController.isFaceAuthEnrolled(userId);
-    }
-
-    /**
      * @return true if there's at least one face enrolled
+     * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
      */
-    public boolean isFaceEnrolled() {
-        return isFaceEnrolled(mSelectedUserInteractor.getSelectedUserId());
+    @Deprecated
+    public boolean isFaceEnabledAndEnrolled() {
+        return getFaceAuthInteractor() != null
+                && getFaceAuthInteractor().isFaceAuthEnabledAndEnrolled();
     }
 
     private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() {
@@ -2798,12 +2467,6 @@
         mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
     }
 
-    private void updateBiometricListeningState(int action,
-            @NonNull FaceAuthUiEvent faceAuthUiEvent) {
-        updateFingerprintListeningState(action);
-        updateFaceListeningState(action, faceAuthUiEvent);
-    }
-
     private void updateFingerprintListeningState(int action) {
         // If this message exists, we should not authenticate again until this message is
         // consumed by the handler
@@ -2859,57 +2522,9 @@
             return;
         }
         mAuthInterruptActive = active;
-        updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
-                FACE_AUTH_TRIGGERED_ON_REACH_GESTURE_ON_AOD);
         requestActiveUnlock(ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE, "onReach");
     }
 
-    /**
-     * Requests face authentication if we're on a state where it's allowed.
-     * This will re-trigger auth in case it fails.
-     * @param reason One of the reasons {@link FaceAuthApiRequestReason} on why this API is being
-     * invoked.
-     * @return current face auth detection state, true if it is running.
-     * @deprecated This is being migrated to use modern architecture.
-     */
-    @Deprecated
-    public boolean requestFaceAuth(@FaceAuthApiRequestReason String reason) {
-        mLogger.logFaceAuthRequested(reason);
-        updateFaceListeningState(BIOMETRIC_ACTION_START, apiRequestReasonToUiEvent(reason));
-        return isFaceDetectionRunning();
-    }
-
-    /**
-     * In case face auth is running, cancel it.
-     */
-    public void cancelFaceAuth() {
-        stopListeningForFace(FACE_AUTH_STOPPED_USER_INPUT_ON_BOUNCER);
-    }
-
-    private void updateFaceListeningState(int action, @NonNull FaceAuthUiEvent faceAuthUiEvent) {
-        if (isFaceAuthInteractorEnabled()) return;
-        // If this message exists, we should not authenticate again until this message is
-        // consumed by the handler
-        if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
-            return;
-        }
-        mHandler.removeCallbacks(mRetryFaceAuthentication);
-        boolean shouldListenForFace = shouldListenForFace();
-        if (mFaceRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFace) {
-            if (action == BIOMETRIC_ACTION_START) {
-                mLogger.v("Ignoring stopListeningForFace()");
-                return;
-            }
-            stopListeningForFace(faceAuthUiEvent);
-        } else if (mFaceRunningState != BIOMETRIC_STATE_RUNNING && shouldListenForFace) {
-            if (action == BIOMETRIC_ACTION_STOP) {
-                mLogger.v("Ignoring startListeningForFace()");
-                return;
-            }
-            startListeningForFace(faceAuthUiEvent);
-        }
-    }
-
     @Nullable
     private InstanceId getKeyguardSessionId() {
         return mSessionTrackerProvider.get().getSessionId(SESSION_KEYGUARD);
@@ -2994,8 +2609,9 @@
             @NonNull ActiveUnlockConfig.ActiveUnlockRequestOrigin requestOrigin,
             String extraReason
     ) {
-        final boolean canFaceBypass = isFaceEnrolled() && mKeyguardBypassController != null
-                && mKeyguardBypassController.canBypass();
+        final boolean canFaceBypass =
+                isFaceEnabledAndEnrolled() && mKeyguardBypassController != null
+                        && mKeyguardBypassController.canBypass();
         requestActiveUnlock(
                 requestOrigin,
                 extraReason, canFaceBypass
@@ -3022,8 +2638,6 @@
     public void setAlternateBouncerShowing(boolean showing) {
         mAlternateBouncerShowing = showing;
         if (mAlternateBouncerShowing) {
-            updateFaceListeningState(BIOMETRIC_ACTION_START,
-                    FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN);
             requestActiveUnlock(
                     ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
                     "alternateBouncer");
@@ -3101,17 +2715,6 @@
                         mSelectedUserInteractor.getSelectedUserId(), false);
     }
 
-    private boolean shouldListenForFaceAssistant() {
-        BiometricAuthenticated face = mUserFaceAuthenticated.get(
-                mSelectedUserInteractor.getSelectedUserId());
-        return mAssistantVisible
-                // There can be intermediate states where mKeyguardShowing is false but
-                // mKeyguardOccluded is true, we don't want to run face auth in such a scenario.
-                && (mKeyguardShowing && mKeyguardOccluded)
-                && !(face != null && face.mAuthenticated)
-                && !mUserHasTrust.get(mSelectedUserInteractor.getSelectedUserId(), false);
-    }
-
     private boolean shouldTriggerActiveUnlockForAssistant() {
         return mAssistantVisible && mKeyguardOccluded
                 && !mUserHasTrust.get(mSelectedUserInteractor.getSelectedUserId(), false);
@@ -3195,107 +2798,14 @@
 
     /**
      * If face auth is allows to scan on this exact moment.
+     *
+     * @deprecated Use {@link KeyguardFaceAuthInteractor#canFaceAuthRun()}
      */
+    @Deprecated
     public boolean shouldListenForFace() {
-        if (mFaceManager == null) {
-            // Device does not have face auth
-            return false;
-        }
-
-        if (isFaceAuthInteractorEnabled()) {
-            return mFaceAuthInteractor.canFaceAuthRun();
-        }
-
-        final boolean statusBarShadeLocked = mStatusBarState == StatusBarState.SHADE_LOCKED;
-        final boolean awakeKeyguard = isKeyguardVisible() && mDeviceInteractive
-                && !statusBarShadeLocked;
-        final int user = mSelectedUserInteractor.getSelectedUserId();
-        final boolean faceAuthAllowed = isUnlockingWithBiometricAllowed(FACE);
-        final boolean canBypass = mKeyguardBypassController != null
-                && mKeyguardBypassController.canBypass();
-        // There's no reason to ask the HAL for authentication when the user can dismiss the
-        // bouncer because the user is trusted, unless we're bypassing and need to auto-dismiss
-        // the lock screen even when TrustAgents are keeping the device unlocked.
-        final boolean userNotTrustedOrDetectionIsNeeded = !getUserHasTrust(user) || canBypass;
-
-        // If the device supports face detection (without authentication), if bypass is enabled,
-        // allow face detection to happen even if stronger auth is required. When face is detected,
-        // we show the bouncer. However, if the user manually locked down the device themselves,
-        // never attempt to detect face.
-        final boolean supportsDetect = isFaceSupported()
-                && mFaceSensorProperties.get(0).supportsFaceDetection
-                && canBypass && !mPrimaryBouncerIsOrWillBeShowing
-                && !isUserInLockdown(user);
-        final boolean faceAuthAllowedOrDetectionIsNeeded = faceAuthAllowed || supportsDetect;
-
-        // If the face or fp has recently been authenticated do not attempt to authenticate again.
-        final boolean faceAndFpNotAuthenticated = !getUserUnlockedWithBiometric(user);
-        final boolean faceDisabledForUser = isFaceDisabled(user);
-        final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
-        final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
-        final boolean isUdfpsFingerDown = mAuthController.isUdfpsFingerDown();
-        final boolean isPostureAllowedForFaceAuth = doesPostureAllowFaceAuth(mPostureState);
-        // Only listen if this KeyguardUpdateMonitor belongs to the system user. There is an
-        // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
-        final boolean shouldListen =
-                (mPrimaryBouncerFullyShown
-                        || mAuthInterruptActive
-                        || mOccludingAppRequestingFace
-                        || awakeKeyguard
-                        || shouldListenForFaceAssistant
-                        || isUdfpsFingerDown
-                        || mAlternateBouncerShowing)
-                && !mSwitchingUser && !faceDisabledForUser && userNotTrustedOrDetectionIsNeeded
-                && !mKeyguardGoingAway && biometricEnabledForUser
-                && faceAuthAllowedOrDetectionIsNeeded && mIsSystemUser
-                && (!mSecureCameraLaunched || mAlternateBouncerShowing)
-                && faceAndFpNotAuthenticated
-                && !mGoingToSleep
-                && isPostureAllowedForFaceAuth
-                && mAllowedDisplayStateWhileAwakeForFaceAuth;
-
-        // Aggregate relevant fields for debug logging.
-        logListenerModelData(
-                new KeyguardFaceListenModel(
-                    System.currentTimeMillis(),
-                    user,
-                    shouldListen,
-                    mAllowedDisplayStateWhileAwakeForFaceAuth,
-                    mAlternateBouncerShowing,
-                    mAuthInterruptActive,
-                    biometricEnabledForUser,
-                    mPrimaryBouncerFullyShown,
-                    faceAndFpNotAuthenticated,
-                    faceAuthAllowed,
-                    faceDisabledForUser,
-                    isFaceLockedOut(),
-                    mGoingToSleep,
-                    awakeKeyguard,
-                    mKeyguardGoingAway,
-                    shouldListenForFaceAssistant,
-                    mOccludingAppRequestingFace,
-                    isPostureAllowedForFaceAuth,
-                    mSecureCameraLaunched,
-                    supportsDetect,
-                    mSwitchingUser,
-                    mIsSystemUser,
-                    isUdfpsFingerDown,
-                    userNotTrustedOrDetectionIsNeeded));
-
-        return shouldListen;
+        return getFaceAuthInteractor() != null && getFaceAuthInteractor().canFaceAuthRun();
     }
 
-    private boolean doesPostureAllowFaceAuth(@DevicePostureInt int posture) {
-        return mConfigFaceAuthSupportedPosture == DEVICE_POSTURE_UNKNOWN
-                || (posture == mConfigFaceAuthSupportedPosture);
-    }
-
-    /**
-     * If the current device posture allows face auth to run.
-     */
-    public boolean doesCurrentPostureAllowFaceAuth() {
-        return doesPostureAllowFaceAuth(mPostureState);
-    }
 
     private void logListenerModelData(@NonNull KeyguardListenModel model) {
         mLogger.logKeyguardListenerModel(model);
@@ -3303,8 +2813,6 @@
             mFingerprintListenBuffer.insert((KeyguardFingerprintListenModel) model);
         } else if (model instanceof KeyguardActiveUnlockModel) {
             mActiveUnlockTriggerBuffer.insert((KeyguardActiveUnlockModel) model);
-        } else if (model instanceof KeyguardFaceListenModel) {
-            mFaceListenBuffer.insert((KeyguardFaceListenModel) model);
         }
     }
 
@@ -3355,85 +2863,16 @@
         }
     }
 
-    private void startListeningForFace(@NonNull FaceAuthUiEvent faceAuthUiEvent) {
-        final int userId = mSelectedUserInteractor.getSelectedUserId();
-        final boolean unlockPossible = isUnlockWithFacePossible(userId);
-        if (mFaceCancelSignal != null) {
-            mLogger.logUnexpectedFaceCancellationSignalState(mFaceRunningState, unlockPossible);
-        }
-
-        if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING) {
-            setFaceRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
-            return;
-        } else if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
-            // Waiting for ERROR_CANCELED before requesting auth again
-            return;
-        }
-        mLogger.logStartedListeningForFace(mFaceRunningState, faceAuthUiEvent);
-        mUiEventLogger.logWithInstanceIdAndPosition(
-                faceAuthUiEvent,
-                0,
-                null,
-                getKeyguardSessionId(),
-                faceAuthUiEvent.getExtraInfo()
-        );
-        mLogger.logFaceUnlockPossible(unlockPossible);
-        if (unlockPossible) {
-            mFaceCancelSignal = new CancellationSignal();
-
-            final FaceAuthenticateOptions faceAuthenticateOptions =
-                    new SysUiFaceAuthenticateOptions(
-                            userId,
-                            faceAuthUiEvent,
-                            faceAuthUiEvent == FACE_AUTH_UPDATED_STARTED_WAKING_UP
-                                    ? faceAuthUiEvent.getExtraInfo()
-                                    : WAKE_REASON_UNKNOWN
-                    ).toFaceAuthenticateOptions();
-            // This would need to be updated for multi-sensor devices
-            final boolean supportsFaceDetection = isFaceSupported()
-                    && mFaceSensorProperties.get(0).supportsFaceDetection;
-            if (!isUnlockingWithBiometricAllowed(FACE)) {
-                final boolean udfpsFingerprintAuthRunning = isUdfpsSupported()
-                        && isFingerprintDetectionRunning();
-                if (supportsFaceDetection && !udfpsFingerprintAuthRunning) {
-                    // Run face detection. (If a face is detected, show the bouncer.)
-                    mLogger.v("startListeningForFace - detect");
-                    mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback,
-                            faceAuthenticateOptions);
-                } else {
-                    // Don't run face detection. Instead, inform the user
-                    // face auth is unavailable and how to proceed.
-                    // (ie: "Use fingerprint instead" or "Swipe up to open")
-                    mLogger.v("Ignoring \"startListeningForFace - detect\". "
-                            + "Informing user face isn't available.");
-                    mFaceAuthenticationCallback.onAuthenticationHelp(
-                            BIOMETRIC_HELP_FACE_NOT_AVAILABLE,
-                            mContext.getResources().getString(
-                                    R.string.keyguard_face_unlock_unavailable)
-                    );
-                    return;
-                }
-            } else {
-                mLogger.v("startListeningForFace - authenticate");
-                final boolean isBypassEnabled = mKeyguardBypassController != null
-                        && mKeyguardBypassController.isBypassEnabled();
-                mFaceManager.authenticate(null /* crypto */, mFaceCancelSignal,
-                        mFaceAuthenticationCallback, null /* handler */,
-                        faceAuthenticateOptions);
-            }
-            setFaceRunningState(BIOMETRIC_STATE_RUNNING);
-        }
-    }
-
     public boolean isFingerprintLockedOut() {
         return mFingerprintLockedOut || mFingerprintLockedOutPermanent;
     }
 
+    /**
+     * @deprecated Use {@link KeyguardFaceAuthInteractor#isLockedOut()}
+     */
+    @Deprecated
     public boolean isFaceLockedOut() {
-        if (isFaceAuthInteractorEnabled()) {
-            return getFaceAuthInteractor().isLockedOut();
-        }
-        return mFaceLockedOutPermanent;
+        return getFaceAuthInteractor() != null && getFaceAuthInteractor().isLockedOut();
     }
 
     /**
@@ -3444,7 +2883,7 @@
      * @return {@code true} if possible.
      */
     public boolean isUnlockingWithBiometricsPossible(int userId) {
-        return isUnlockWithFacePossible(userId) || isUnlockWithFingerprintPossible(userId);
+        return isUnlockWithFacePossible() || isUnlockWithFingerprintPossible(userId);
     }
 
     /**
@@ -3455,8 +2894,12 @@
      * @return {@code true} if possible.
      */
     public boolean isUnlockingWithNonStrongBiometricsPossible(int userId) {
-        return (!isFaceClass3() && isUnlockWithFacePossible(userId))
-                || (isFingerprintClass3() && isUnlockWithFingerprintPossible(userId));
+        if (getFaceAuthInteractor() != null && !getFaceAuthInteractor().isFaceAuthStrong()) {
+            if (isUnlockWithFacePossible()) {
+                return true;
+            }
+        }
+        return isFingerprintClass3() && isUnlockWithFingerprintPossible(userId);
     }
 
     @SuppressLint("MissingPermission")
@@ -3466,16 +2909,13 @@
     }
 
     /**
-     * @deprecated This is being migrated to use modern architecture.
+     * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
      */
     @VisibleForTesting
     @Deprecated
-    public boolean isUnlockWithFacePossible(int userId) {
-        if (isFaceAuthInteractorEnabled()) {
-            return getFaceAuthInteractor() != null
+    public boolean isUnlockWithFacePossible() {
+        return getFaceAuthInteractor() != null
                     && getFaceAuthInteractor().isFaceAuthEnabledAndEnrolled();
-        }
-        return isFaceSupported() && isFaceEnrolled(userId) && !isFaceDisabled(userId);
     }
 
     private void notifyAboutEnrollmentChange(@BiometricAuthenticator.Modality int modality) {
@@ -3513,25 +2953,6 @@
         }
     }
 
-    private void stopListeningForFace(@NonNull FaceAuthUiEvent faceAuthUiEvent) {
-        if (isFaceAuthInteractorEnabled()) return;
-        mLogger.v("stopListeningForFace()");
-        mLogger.logStoppedListeningForFace(mFaceRunningState, faceAuthUiEvent.getReason());
-        mUiEventLogger.log(faceAuthUiEvent, getKeyguardSessionId());
-        if (mFaceRunningState == BIOMETRIC_STATE_RUNNING) {
-            if (mFaceCancelSignal != null) {
-                mFaceCancelSignal.cancel();
-                mFaceCancelSignal = null;
-                mHandler.removeCallbacks(mFaceCancelNotReceived);
-                mHandler.postDelayed(mFaceCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT);
-            }
-            setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
-        }
-        if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
-            setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
-        }
-    }
-
     private boolean isDeviceProvisionedInSettingsDb() {
         return Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
@@ -3617,13 +3038,6 @@
             }
         }
 
-        // Immediately stop previous biometric listening states.
-        // Resetting lockout states updates the biometric listening states.
-        if (isFaceSupported()) {
-            stopListeningForFace(FACE_AUTH_UPDATED_USER_SWITCHING);
-            handleFaceLockoutReset(mFaceManager.getLockoutModeForUser(
-                    mFaceSensorProperties.get(0).sensorId, userId));
-        }
         if (isFingerprintSupported()) {
             stopListeningForFingerprint();
             handleFingerprintLockoutReset(mFpm.getLockoutModeForUser(
@@ -3866,8 +3280,7 @@
     @VisibleForTesting
     protected void handleKeyguardReset() {
         mLogger.d("handleKeyguardReset");
-        updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                FACE_AUTH_UPDATED_KEYGUARD_RESET);
+        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
         mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition();
     }
 
@@ -3935,8 +3348,6 @@
                     cb.onKeyguardBouncerFullyShowingChanged(mPrimaryBouncerFullyShown);
                 }
             }
-            updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
-                    FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN);
         }
     }
 
@@ -4071,8 +3482,7 @@
         }
         mSwitchingUser = switching;
         // Since this comes in on a binder thread, we need to post it first
-        mHandler.post(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
-                FACE_AUTH_UPDATED_USER_SWITCHING));
+        mHandler.post(() -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE));
     }
 
     private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
@@ -4161,7 +3571,6 @@
     private void clearBiometricRecognized(int unlockedUser) {
         Assert.isMainThread();
         mUserFingerprintAuthenticated.clear();
-        mUserFaceAuthenticated.clear();
         mTrustManager.clearAllBiometricRecognized(FINGERPRINT, unlockedUser);
         mTrustManager.clearAllBiometricRecognized(FACE, unlockedUser);
         mLogger.d("clearBiometricRecognized");
@@ -4394,12 +3803,6 @@
         return isFingerprintSupported() && isClass3Biometric(mFingerprintSensorProperties.get(0));
     }
 
-    @VisibleForTesting
-    protected boolean isFaceClass3() {
-        // This assumes that there is at most one face sensor property
-        return isFaceSupported() && isClass3Biometric(mFaceSensorProperties.get(0));
-    }
-
     private boolean isClass3Biometric(SensorPropertiesInternal sensorProperties) {
         return sensorProperties.sensorStrength == SensorProperties.STRENGTH_STRONG;
     }
@@ -4411,8 +3814,8 @@
         mStatusBarStateController.removeCallback(mStatusBarStateControllerListener);
         mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener);
         mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener);
-        if (isFaceAuthInteractorEnabled()) {
-            mFaceAuthInteractor.unregisterListener(mFaceAuthenticationListener);
+        if (getFaceAuthInteractor() != null) {
+            getFaceAuthInteractor().unregisterListener(mFaceAuthenticationListener);
         }
 
         if (mDeviceProvisionedObserver != null) {
@@ -4432,7 +3835,6 @@
 
         mLockPatternUtils.unregisterStrongAuthTracker(mStrongAuthTracker);
         mTrustManager.unregisterTrustListener(this);
-        mDisplayTracker.removeCallback(mDisplayCallback);
 
         mHandler.removeCallbacksAndMessages(null);
     }
@@ -4446,7 +3848,6 @@
                 mSelectedUserInteractor.getSelectedUserId()));
         pw.println("  getUserUnlockedWithBiometric()="
                 + getUserUnlockedWithBiometric(mSelectedUserInteractor.getSelectedUserId()));
-        pw.println("  isFaceAuthInteractorEnabled: " + isFaceAuthInteractorEnabled());
         pw.println("  SIM States:");
         for (SimData data : mSimDatas.values()) {
             pw.println("    " + data.toString());
@@ -4519,50 +3920,11 @@
                     mFingerprintListenBuffer.toList()
             ).printTableData(pw);
         }
-        if (isFaceSupported()) {
-            final int userId = mSelectedUserInteractor.getSelectedUserId(true);
-            final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
-            BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
-            pw.println("  Face authentication state (user=" + userId + ")");
-            pw.println("    isFaceClass3=" + isFaceClass3());
-            pw.println("    allowed="
-                    + (face != null && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric)));
-            pw.println("    auth'd="
-                    + (face != null && face.mAuthenticated));
-            pw.println("    authSinceBoot="
-                    + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
-            pw.println("    disabled(DPM)=" + isFaceDisabled(userId));
-            pw.println("    possible=" + isUnlockWithFacePossible(userId));
-            pw.println("    listening: actual=" + mFaceRunningState
-                    + " expected=(" + (shouldListenForFace() ? 1 : 0));
-            pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
-            pw.println("    isNonStrongBiometricAllowedAfterIdleTimeout="
-                    + mStrongAuthTracker.isNonStrongBiometricAllowedAfterIdleTimeout(userId));
-            pw.println("    trustManaged=" + getUserTrustIsManaged(userId));
-            pw.println("    mFaceLockedOutPermanent=" + mFaceLockedOutPermanent);
-            pw.println("    enabledByUser=" + mBiometricEnabledForUser.get(userId));
-            pw.println("    mSecureCameraLaunched=" + mSecureCameraLaunched);
-            pw.println("    mPrimaryBouncerFullyShown=" + mPrimaryBouncerFullyShown);
-            pw.println("    mNeedsSlowUnlockTransition=" + mNeedsSlowUnlockTransition);
-            new DumpsysTableLogger(
-                    "KeyguardFaceListen",
-                    KeyguardFaceListenModel.TABLE_HEADERS,
-                    mFaceListenBuffer.toList()
-            ).printTableData(pw);
-        } else if (mFaceManager != null && mFaceSensorProperties.isEmpty()) {
-            final int userId = mSelectedUserInteractor.getSelectedUserId(true);
-            pw.println("  Face state (user=" + userId + ")");
-            pw.println("    mFaceSensorProperties.isEmpty="
-                    + mFaceSensorProperties.isEmpty());
-            pw.println("    mFaceManager.isHardwareDetected="
-                    + mFaceManager.isHardwareDetected());
-
-            new DumpsysTableLogger(
-                    "KeyguardFaceListen",
-                    KeyguardFingerprintListenModel.TABLE_HEADERS,
-                    mFingerprintListenBuffer.toList()
-            ).printTableData(pw);
-        }
+        final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+        final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
+        pw.println("    authSinceBoot="
+                + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
+        pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
         pw.println("ActiveUnlockRunning="
                 + mTrustManager.isActiveUnlockRunning(mSelectedUserInteractor.getSelectedUserId()));
         new DumpsysTableLogger(
@@ -4577,13 +3939,8 @@
      * Cancels all operations in the scheduler if it is hung for 10 seconds.
      */
     public void startBiometricWatchdog() {
-        final boolean isFaceAuthInteractorEnabled = isFaceAuthInteractorEnabled();
         mBackgroundExecutor.execute(() -> {
             Trace.beginSection("#startBiometricWatchdog");
-            if (mFaceManager != null && !isFaceAuthInteractorEnabled) {
-                mLogger.scheduleWatchdog("face");
-                mFaceManager.scheduleWatchdog();
-            }
             if (mFpm != null) {
                 mLogger.scheduleWatchdog("fingerprint");
                 mFpm.scheduleWatchdog();
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 5bf8d63..055ca56 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -20,14 +20,12 @@
 import android.hardware.biometrics.BiometricConstants.LockoutMode
 import android.hardware.biometrics.BiometricSourceType
 import android.os.PowerManager
-import android.os.PowerManager.WakeReason
 import android.telephony.ServiceState
 import android.telephony.SubscriptionInfo
 import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
 import android.telephony.TelephonyManager
 import com.android.keyguard.ActiveUnlockConfig
-import com.android.keyguard.FaceAuthUiEvent
 import com.android.keyguard.KeyguardListenModel
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.keyguard.TrustGrantFlags
@@ -102,14 +100,6 @@
         logBuffer.log(TAG, ERROR, {}, { logMsg }, exception = ex)
     }
 
-    fun logFaceAuthDisabledForUser(userId: Int) {
-        logBuffer.log(
-            TAG,
-            DEBUG,
-            { int1 = userId },
-            { "Face authentication disabled by DPM for userId: $int1" }
-        )
-    }
     fun logFaceAuthError(msgId: Int, originalErrMsg: String) {
         logBuffer.log(
             TAG,
@@ -131,31 +121,10 @@
         )
     }
 
-    fun logFaceAuthRequested(reason: String?) {
-        logBuffer.log(TAG, DEBUG, { str1 = reason }, { "requestFaceAuth() reason=$str1" })
-    }
-
     fun logFaceAuthSuccess(userId: Int) {
         logBuffer.log(TAG, DEBUG, { int1 = userId }, { "Face auth succeeded for user $int1" })
     }
 
-    fun logFaceLockoutReset(@LockoutMode mode: Int) {
-        logBuffer.log(TAG, DEBUG, { int1 = mode }, { "handleFaceLockoutReset: $int1" })
-    }
-
-    fun logFaceRunningState(faceRunningState: Int) {
-        logBuffer.log(TAG, DEBUG, { int1 = faceRunningState }, { "faceRunningState: $int1" })
-    }
-
-    fun logFaceUnlockPossible(isFaceUnlockPossible: Boolean) {
-        logBuffer.log(
-            TAG,
-            DEBUG,
-            { bool1 = isFaceUnlockPossible },
-            { "isUnlockWithFacePossible: $bool1" }
-        )
-    }
-
     fun logFingerprintAuthForWrongUser(authUserId: Int) {
         logBuffer.log(
             FP_LOG_TAG,
@@ -301,15 +270,6 @@
         logBuffer.log(TAG, VERBOSE, { str1 = "$callback" }, { "*** register callback for $str1" })
     }
 
-    fun logRetryingAfterFaceHwUnavailable(retryCount: Int) {
-        logBuffer.log(
-            TAG,
-            WARNING,
-            { int1 = retryCount },
-            { "Retrying face after HW unavailable, attempt $int1" }
-        )
-    }
-
     fun logRetryAfterFpErrorWithDelay(msgId: Int, errString: String?, delay: Int) {
         logBuffer.log(
             TAG,
@@ -419,43 +379,6 @@
         logBuffer.log(TAG, VERBOSE, { int1 = subId }, { "reportSimUnlocked(subId=$int1)" })
     }
 
-    fun logStartedListeningForFace(faceRunningState: Int, faceAuthUiEvent: FaceAuthUiEvent) {
-        logBuffer.log(
-            TAG,
-            VERBOSE,
-            {
-                int1 = faceRunningState
-                str1 = faceAuthUiEvent.reason
-                str2 = faceAuthUiEvent.extraInfoToString()
-            },
-            { "startListeningForFace(): $int1, reason: $str1 $str2" }
-        )
-    }
-
-    fun logStartedListeningForFaceFromWakeUp(faceRunningState: Int, @WakeReason pmWakeReason: Int) {
-        logBuffer.log(
-            TAG,
-            VERBOSE,
-            {
-                int1 = faceRunningState
-                str1 = PowerManager.wakeReasonToString(pmWakeReason)
-            },
-            { "startListeningForFace(): $int1, reason: wakeUp-$str1" }
-        )
-    }
-
-    fun logStoppedListeningForFace(faceRunningState: Int, faceAuthReason: String) {
-        logBuffer.log(
-            TAG,
-            VERBOSE,
-            {
-                int1 = faceRunningState
-                str1 = faceAuthReason
-            },
-            { "stopListeningForFace(): currentFaceRunningState: $int1, reason: $str1" }
-        )
-    }
-
     fun logSubInfo(subInfo: SubscriptionInfo?) {
         logBuffer.log(TAG, DEBUG, { str1 = "$subInfo" }, { "SubInfo:$str1" })
     }
@@ -476,22 +399,6 @@
         logBuffer.log(TAG, DEBUG, { int1 = sensorId }, { "onUdfpsPointerUp, sensorId: $int1" })
     }
 
-    fun logUnexpectedFaceCancellationSignalState(faceRunningState: Int, unlockPossible: Boolean) {
-        logBuffer.log(
-            TAG,
-            ERROR,
-            {
-                int1 = faceRunningState
-                bool1 = unlockPossible
-            },
-            {
-                "Cancellation signal is not null, high chance of bug in " +
-                    "face auth lifecycle management. " +
-                    "Face state: $int1, unlockPossible: $bool1"
-            }
-        )
-    }
-
     fun logUnexpectedFpCancellationSignalState(
         fingerprintRunningState: Int,
         unlockPossible: Boolean
@@ -588,15 +495,6 @@
         )
     }
 
-    fun logSkipUpdateFaceListeningOnWakeup(@WakeReason pmWakeReason: Int) {
-        logBuffer.log(
-            TAG,
-            VERBOSE,
-            { str1 = PowerManager.wakeReasonToString(pmWakeReason) },
-            { "Skip updating face listening state on wakeup from $str1" }
-        )
-    }
-
     fun logTaskStackChangedForAssistant(assistantVisible: Boolean) {
         logBuffer.log(
             TAG,
@@ -648,18 +546,6 @@
         )
     }
 
-    fun logFaceEnrolledUpdated(oldValue: Boolean, newValue: Boolean) {
-        logBuffer.log(
-            TAG,
-            DEBUG,
-            {
-                bool1 = oldValue
-                bool2 = newValue
-            },
-            { "Face enrolled state changed: old: $bool1, new: $bool2" }
-        )
-    }
-
     fun logTrustUsuallyManagedUpdated(
         userId: Int,
         oldValue: Boolean,
@@ -745,18 +631,6 @@
         )
     }
 
-    fun logFingerprintHelp(helpMsgId: Int, helpString: CharSequence) {
-        logBuffer.log(
-            FP_LOG_TAG,
-            DEBUG,
-            {
-                int1 = helpMsgId
-                str1 = "$helpString"
-            },
-            { "fingerprint help message: $int1, $str1" }
-        )
-    }
-
     fun logFingerprintAcquired(acquireInfo: Int) {
         logBuffer.log(
             FP_LOG_TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 57e252d..8fe42b5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -100,7 +100,6 @@
 import javax.inject.Provider;
 
 import kotlin.Unit;
-
 import kotlinx.coroutines.CoroutineScope;
 
 /**
@@ -1099,6 +1098,7 @@
         // TODO(b/141025588): Create separate methods for handling hard and soft errors.
         final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
                 || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT
+                || error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL
                 || isCameraPrivacyEnabled);
         if (mCurrentDialog != null) {
             if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
index bbdcadb..cb75049 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
@@ -20,12 +20,10 @@
 import android.os.Bundle
 import android.view.View
 import android.view.accessibility.AccessibilityNodeInfo
-import com.android.keyguard.FaceAuthApiRequestReason
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.res.R
 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.res.R
 import javax.inject.Inject
 
 /**
@@ -37,12 +35,11 @@
 @Inject
 constructor(
     @Main private val resources: Resources,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val faceAuthInteractor: KeyguardFaceAuthInteractor,
 ) : View.AccessibilityDelegate() {
     override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
         super.onInitializeAccessibilityNodeInfo(host, info)
-        if (keyguardUpdateMonitor.shouldListenForFace()) {
+        if (faceAuthInteractor.canFaceAuthRun()) {
             val clickActionToRetryFace =
                 AccessibilityNodeInfo.AccessibilityAction(
                     AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id,
@@ -54,7 +51,6 @@
 
     override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean {
         return if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id) {
-            keyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.ACCESSIBILITY_ACTION)
             faceAuthInteractor.onAccessibilityAction()
             true
         } else super.performAccessibilityAction(host, action, args)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index b064391..85a119c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -52,6 +52,7 @@
 import android.view.HapticFeedbackConstants;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.View;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
@@ -63,7 +64,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
 import com.android.internal.util.LatencyTracker;
-import com.android.keyguard.FaceAuthApiRequestReason;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -78,9 +78,9 @@
 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.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.doze.DozeReceiver;
 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;
@@ -150,7 +150,6 @@
     @NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
     @NonNull private final Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
     @NonNull private final VibratorHelper mVibrator;
-    @NonNull private final FeatureFlags mFeatureFlags;
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final PowerManager mPowerManager;
     @NonNull private final AccessibilityManager mAccessibilityManager;
@@ -281,7 +280,6 @@
                             fromUdfpsView
                         ),
                         mActivityLaunchAnimator,
-                        mFeatureFlags,
                         mPrimaryBouncerInteractor,
                         mAlternateBouncerInteractor,
                         mUdfpsKeyguardAccessibilityDelegate,
@@ -318,10 +316,8 @@
                         return;
                     }
                     mAcquiredReceived = true;
-                    final UdfpsView view = mOverlay.getOverlayView();
-                    if (view != null && isOptical()) {
-                        unconfigureDisplay(view);
-                    }
+                    final View view = mOverlay.getTouchOverlay();
+                    unconfigureDisplay(view);
                     tryAodSendFingerUp();
                 });
             }
@@ -339,7 +335,9 @@
                 if (mOverlay == null || mOverlay.isHiding()) {
                     return;
                 }
-                mOverlay.getOverlayView().setDebugMessage(message);
+                if (!DeviceEntryUdfpsRefactor.isEnabled()) {
+                    ((UdfpsView) mOverlay.getTouchOverlay()).setDebugMessage(message);
+                }
             });
         }
 
@@ -506,6 +504,7 @@
         if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f
                 && !mAlternateBouncerInteractor.isVisibleState())
                 || mPrimaryBouncerInteractor.isInTransit()) {
+            Log.w(TAG, "ignoring touch due to qsDragProcess or primaryBouncerInteractor");
             return false;
         }
         if (event.getAction() == MotionEvent.ACTION_DOWN
@@ -563,7 +562,7 @@
                 }
                 mAttemptedToDismissKeyguard = false;
                 onFingerUp(requestId,
-                        mOverlay.getOverlayView(),
+                        mOverlay.getTouchOverlay(),
                         data.getPointerId(),
                         data.getX(),
                         data.getY(),
@@ -597,7 +596,7 @@
         if (shouldPilfer && !mPointerPilfered
                 && getBiometricSessionType() != SESSION_BIOMETRIC_PROMPT) {
             mInputManager.pilferPointers(
-                    mOverlay.getOverlayView().getViewRootImpl().getInputToken());
+                    mOverlay.getTouchOverlay().getViewRootImpl().getInputToken());
             mPointerPilfered = true;
         }
 
@@ -605,9 +604,15 @@
     }
 
     private boolean shouldTryToDismissKeyguard() {
-        return mOverlay != null
-                && mOverlay.getAnimationViewController()
-                instanceof UdfpsKeyguardViewControllerAdapter
+        boolean onKeyguard = false;
+        if (DeviceEntryUdfpsRefactor.isEnabled()) {
+            onKeyguard = mKeyguardStateController.isShowing();
+        } else {
+            onKeyguard = mOverlay != null
+                    && mOverlay.getAnimationViewController()
+                        instanceof UdfpsKeyguardViewControllerAdapter;
+        }
+        return onKeyguard
                 && mKeyguardStateController.canDismissLockScreen()
                 && !mAttemptedToDismissKeyguard;
     }
@@ -623,7 +628,6 @@
             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             @NonNull DumpManager dumpManager,
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
-            @NonNull FeatureFlags featureFlags,
             @NonNull FalsingManager falsingManager,
             @NonNull PowerManager powerManager,
             @NonNull AccessibilityManager accessibilityManager,
@@ -670,7 +674,6 @@
         mDumpManager = dumpManager;
         mDialogManager = dialogManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mFeatureFlags = featureFlags;
         mFalsingManager = falsingManager;
         mPowerManager = powerManager;
         mAccessibilityManager = accessibilityManager;
@@ -737,9 +740,9 @@
     @VisibleForTesting
     public void playStartHaptic() {
         if (mAccessibilityManager.isTouchExplorationEnabled()) {
-            if (mOverlay != null && mOverlay.getOverlayView() != null) {
+            if (mOverlay != null && mOverlay.getTouchOverlay() != null) {
                 mVibrator.performHapticFeedback(
-                        mOverlay.getOverlayView(),
+                        mOverlay.getTouchOverlay(),
                         HapticFeedbackConstants.CONTEXT_CLICK
                 );
             } else {
@@ -751,10 +754,11 @@
 
     @Override
     public void dozeTimeTick() {
-        if (mOverlay != null) {
-            final UdfpsView view = mOverlay.getOverlayView();
+        if (mOverlay != null && mOverlay.getTouchOverlay() instanceof UdfpsView) {
+            DeviceEntryUdfpsRefactor.assertInLegacyMode();
+            final View view = mOverlay.getTouchOverlay();
             if (view != null) {
-                view.dozeTimeTick();
+                ((UdfpsView) view).dozeTimeTick();
             }
         }
     }
@@ -797,7 +801,7 @@
 
         if (mOverlay != null) {
             // Reset the controller back to its starting state.
-            final UdfpsView oldView = mOverlay.getOverlayView();
+            final View oldView = mOverlay.getTouchOverlay();
             if (oldView != null) {
                 onFingerUp(mOverlay.getRequestId(), oldView);
             }
@@ -813,9 +817,21 @@
 
     }
 
-    private void unconfigureDisplay(@NonNull UdfpsView view) {
-        if (view.isDisplayConfigured()) {
-            view.unconfigureDisplay();
+    private void unconfigureDisplay(View view) {
+        if (!isOptical()) {
+            return;
+        }
+        if (DeviceEntryUdfpsRefactor.isEnabled()) {
+            if (mUdfpsDisplayMode != null) {
+                mUdfpsDisplayMode.disable(null); // beverlt
+            }
+        } else {
+            if (view != null) {
+                UdfpsView udfpsView = (UdfpsView) view;
+                if (udfpsView.isDisplayConfigured()) {
+                    udfpsView.unconfigureDisplay();
+                }
+            }
         }
     }
 
@@ -837,10 +853,10 @@
             }
             mKeyguardViewManager.showPrimaryBouncer(true);
 
-            // play the same haptic as the LockIconViewController longpress
-            if (mOverlay != null && mOverlay.getOverlayView() != null) {
+            // play the same haptic as the DeviceEntryIcon longpress
+            if (mOverlay != null && mOverlay.getTouchOverlay() != null) {
                 mVibrator.performHapticFeedback(
-                        mOverlay.getOverlayView(),
+                        mOverlay.getTouchOverlay(),
                         UdfpsController.LONG_PRESS
                 );
             } else {
@@ -909,8 +925,8 @@
             return;
         }
         cancelAodSendFingerUpAction();
-        if (mOverlay != null && mOverlay.getOverlayView() != null) {
-            onFingerUp(mOverlay.getRequestId(), mOverlay.getOverlayView());
+        if (mOverlay != null && mOverlay.getTouchOverlay() != null) {
+            onFingerUp(mOverlay.getRequestId(), mOverlay.getTouchOverlay());
         }
     }
 
@@ -996,20 +1012,22 @@
             playStartHaptic();
 
             mKeyguardFaceAuthInteractor.onUdfpsSensorTouched();
-            if (!mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
-                mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
-            }
         }
         mOnFingerDown = true;
         mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
                 minor, major, orientation, time, gestureStart, isAod);
         Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
-        final UdfpsView view = mOverlay.getOverlayView();
+
+        final View view = mOverlay.getTouchOverlay();
         if (view != null && isOptical()) {
             if (mIgnoreRefreshRate) {
                 dispatchOnUiReady(requestId);
             } else {
-                view.configureDisplay(() -> dispatchOnUiReady(requestId));
+                if (DeviceEntryUdfpsRefactor.isEnabled()) {
+                    mUdfpsDisplayMode.enable(() -> dispatchOnUiReady(requestId));
+                } else {
+                    ((UdfpsView) view).configureDisplay(() -> dispatchOnUiReady(requestId));
+                }
             }
         }
 
@@ -1018,7 +1036,7 @@
         }
     }
 
-    private void onFingerUp(long requestId, @NonNull UdfpsView view) {
+    private void onFingerUp(long requestId, @NonNull View view) {
         onFingerUp(
                 requestId,
                 view,
@@ -1035,7 +1053,7 @@
 
     private void onFingerUp(
             long requestId,
-            @NonNull UdfpsView view,
+            View view,
             int pointerId,
             float x,
             float y,
@@ -1056,9 +1074,7 @@
             }
         }
         mOnFingerDown = false;
-        if (isOptical()) {
-            unconfigureDisplay(view);
-        }
+        unconfigureDisplay(view);
         cancelAodSendFingerUpAction();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index a5bd89a..2d54f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -46,11 +46,11 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
+import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -96,7 +96,6 @@
     private val controllerCallback: IUdfpsOverlayControllerCallback,
     private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
     private val activityLaunchAnimator: ActivityLaunchAnimator,
-    private val featureFlags: FeatureFlags,
     private val primaryBouncerInteractor: PrimaryBouncerInteractor,
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
     private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
@@ -104,9 +103,22 @@
     private val transitionInteractor: KeyguardTransitionInteractor,
     private val selectedUserInteractor: SelectedUserInteractor,
 ) {
-    /** The view, when [isShowing], or null. */
-    var overlayView: UdfpsView? = null
+    private var overlayViewLegacy: UdfpsView? = null
         private set
+    private var overlayTouchView: UdfpsTouchOverlay? = null
+
+    /**
+     * Get the current UDFPS overlay touch view which is a different View depending on whether
+     * the DeviceEntryUdfpsRefactor flag is enabled or not.
+     * @return The view, when [isShowing], else null
+     */
+    fun getTouchOverlay(): View? {
+        return if (DeviceEntryUdfpsRefactor.isEnabled) {
+            overlayTouchView
+        } else {
+            overlayViewLegacy
+        }
+    }
 
     private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
     private var sensorBounds: Rect = Rect()
@@ -132,15 +144,15 @@
 
     /** If the overlay is currently showing. */
     val isShowing: Boolean
-        get() = overlayView != null
+        get() = getTouchOverlay() != null
 
     /** Opposite of [isShowing]. */
     val isHiding: Boolean
-        get() = overlayView == null
+        get() = getTouchOverlay() == null
 
     /** The animation controller if the overlay [isShowing]. */
     val animationViewController: UdfpsAnimationViewController<*>?
-        get() = overlayView?.animationViewController
+        get() = overlayViewLegacy?.animationViewController
 
     private var touchExplorationEnabled = false
 
@@ -158,28 +170,48 @@
     /** Show the overlay or return false and do nothing if it is already showing. */
     @SuppressLint("ClickableViewAccessibility")
     fun show(controller: UdfpsController, params: UdfpsOverlayParams): Boolean {
-        if (overlayView == null) {
+        if (getTouchOverlay() == null) {
             overlayParams = params
             sensorBounds = Rect(params.sensorBounds)
             try {
-                overlayView = (inflater.inflate(
-                    R.layout.udfps_view, null, false
-                ) as UdfpsView).apply {
-                    overlayParams = params
-                    setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
-                    val animation = inflateUdfpsAnimation(this, controller)
-                    if (animation != null) {
-                        animation.init()
-                        animationViewController = animation
+                if (DeviceEntryUdfpsRefactor.isEnabled) {
+                    overlayTouchView = (inflater.inflate(
+                            R.layout.udfps_touch_overlay, null, false
+                    ) as UdfpsTouchOverlay).apply {
+                        // This view overlaps the sensor area
+                        // prevent it from being selectable during a11y
+                        if (requestReason.isImportantForAccessibility()) {
+                            importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+                        }
+                        windowManager.addView(this, coreLayoutParams.updateDimensions(null))
                     }
-                    // This view overlaps the sensor area
-                    // prevent it from being selectable during a11y
-                    if (requestReason.isImportantForAccessibility()) {
-                        importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
-                    }
+                    // TODO (b/305234447): Bind view model to UdfpsTouchOverlay here to control
+                    // the visibility (sometimes, even if UDFPS is running, the UDFPS UI can be
+                    // obscured and we don't want to accept touches. ie: for enrollment don't accept
+                    // touches when the shade is expanded and for keyguard: don't accept touches
+                    // depending on the keyguard & shade state
+                } else {
+                    overlayViewLegacy = (inflater.inflate(
+                            R.layout.udfps_view, null, false
+                    ) as UdfpsView).apply {
+                        overlayParams = params
+                        setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
+                        val animation = inflateUdfpsAnimation(this, controller)
+                        if (animation != null) {
+                            animation.init()
+                            animationViewController = animation
+                        }
+                        // This view overlaps the sensor area
+                        // prevent it from being selectable during a11y
+                        if (requestReason.isImportantForAccessibility()) {
+                            importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+                        }
 
-                    windowManager.addView(this, coreLayoutParams.updateDimensions(animation))
-                    sensorRect = sensorBounds
+                        windowManager.addView(this, coreLayoutParams.updateDimensions(animation))
+                        sensorRect = sensorBounds
+                    }
+                }
+                getTouchOverlay()?.apply {
                     touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled
                     overlayTouchListener = TouchExplorationStateChangeListener {
                         if (accessibilityManager.isTouchExplorationEnabled) {
@@ -193,7 +225,7 @@
                         }
                     }
                     accessibilityManager.addTouchExplorationStateChangeListener(
-                        overlayTouchListener!!
+                            overlayTouchListener!!
                     )
                     overlayTouchListener?.onTouchExplorationStateChanged(true)
                 }
@@ -211,6 +243,8 @@
         view: UdfpsView,
         controller: UdfpsController
     ): UdfpsAnimationViewController<*>? {
+        DeviceEntryUdfpsRefactor.assertInLegacyMode()
+
         val isEnrollment = when (requestReason) {
             REASON_ENROLL_FIND_SENSOR, REASON_ENROLL_ENROLLING -> true
             else -> false
@@ -237,39 +271,27 @@
                 )
             }
             REASON_AUTH_KEYGUARD -> {
-                if (DeviceEntryUdfpsRefactor.isEnabled) {
-                    // note: empty controller, currently shows no visual affordance
-                    // instead SysUI will show the fingerprint icon in its DeviceEntryIconView
-                    UdfpsBpViewController(
-                            view.addUdfpsView(R.layout.udfps_bp_view),
-                            statusBarStateController,
-                            primaryBouncerInteractor,
-                            dialogManager,
-                            dumpManager
-                    )
-                } else {
-                    UdfpsKeyguardViewControllerLegacy(
-                        view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
-                            updateSensorLocation(sensorBounds)
-                        },
-                        statusBarStateController,
-                        statusBarKeyguardViewManager,
-                        keyguardUpdateMonitor,
-                        dumpManager,
-                        transitionController,
-                        configurationController,
-                        keyguardStateController,
-                        unlockedScreenOffAnimationController,
-                        dialogManager,
-                        controller,
-                        activityLaunchAnimator,
-                        primaryBouncerInteractor,
-                        alternateBouncerInteractor,
-                        udfpsKeyguardAccessibilityDelegate,
-                        selectedUserInteractor,
-                        transitionInteractor,
-                    )
-                }
+                UdfpsKeyguardViewControllerLegacy(
+                    view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
+                        updateSensorLocation(sensorBounds)
+                    },
+                    statusBarStateController,
+                    statusBarKeyguardViewManager,
+                    keyguardUpdateMonitor,
+                    dumpManager,
+                    transitionController,
+                    configurationController,
+                    keyguardStateController,
+                    unlockedScreenOffAnimationController,
+                    dialogManager,
+                    controller,
+                    activityLaunchAnimator,
+                    primaryBouncerInteractor,
+                    alternateBouncerInteractor,
+                    udfpsKeyguardAccessibilityDelegate,
+                    selectedUserInteractor,
+                    transitionInteractor,
+                )
             }
             REASON_AUTH_BP -> {
                 // note: empty controller, currently shows no visual affordance
@@ -302,19 +324,26 @@
     fun hide(): Boolean {
         val wasShowing = isShowing
 
-        overlayView?.apply {
+        overlayViewLegacy?.apply {
             if (isDisplayConfigured) {
                 unconfigureDisplay()
             }
+            animationViewController = null
+        }
+        if (DeviceEntryUdfpsRefactor.isEnabled) {
+            udfpsDisplayModeProvider.disable(null)
+        }
+        getTouchOverlay()?.apply {
             windowManager.removeView(this)
             setOnTouchListener(null)
             setOnHoverListener(null)
-            animationViewController = null
             overlayTouchListener?.let {
                 accessibilityManager.removeTouchExplorationStateChangeListener(it)
             }
         }
-        overlayView = null
+
+        overlayViewLegacy = null
+        overlayTouchView = null
         overlayTouchListener = null
 
         return wasShowing
@@ -392,7 +421,14 @@
     }
 
     private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean {
-        if (animation !is UdfpsKeyguardViewControllerAdapter) {
+        val keyguardNotShowing =
+            if (DeviceEntryUdfpsRefactor.isEnabled) {
+                !keyguardStateController.isShowing
+            } else {
+                animation !is UdfpsKeyguardViewControllerAdapter
+            }
+
+        if (keyguardNotShowing) {
             // always rotate view if we're not on the keyguard
             return true
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 35c3ded..6954eb6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -50,7 +50,6 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.launch
 
 /** Class that coordinates non-HBM animations during keyguard authentication. */
@@ -215,12 +214,12 @@
     suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job {
         return scope.launch {
             transitionInteractor.dozeAmountTransition.collect { transitionStep ->
-                  view.onDozeAmountChanged(
-                      transitionStep.value,
-                      transitionStep.value,
-                      UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
-                  )
-              }
+                view.onDozeAmountChanged(
+                    transitionStep.value,
+                    transitionStep.value,
+                    UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
+                )
+            }
         }
     }
 
@@ -286,7 +285,6 @@
         keyguardStateController.removeCallback(keyguardStateControllerCallback)
         statusBarStateController.removeCallback(stateListener)
         keyguardViewManager.removeOccludingAppBiometricUI(occludingAppBiometricUI)
-        keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false)
         configurationController.removeCallback(configurationListener)
         if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) {
             lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null
@@ -334,14 +332,9 @@
             if (udfpsAffordanceWasNotShowing) {
                 view.animateInUdfpsBouncer(null)
             }
-            if (keyguardStateController.isOccluded) {
-                keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true)
-            }
             view.announceForAccessibility(
                 view.context.getString(R.string.accessibility_fingerprint_bouncer)
             )
-        } else {
-            keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false)
         }
         updateAlpha()
         updatePauseAuth()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/view/UdfpsTouchOverlay.kt
similarity index 61%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/biometrics/ui/view/UdfpsTouchOverlay.kt
index a780763..2484c33 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/view/UdfpsTouchOverlay.kt
@@ -13,9 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.systemui.biometrics.ui.view
 
-package com.android.systemui.flags
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+/**
+ * A translucent (not visible to the user) view that receives touches to send to FingerprintManager
+ * for fingerprint authentication.
+ */
+class UdfpsTouchOverlay(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs)
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 56dfa5ed..aa7758f 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
@@ -39,6 +39,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 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
@@ -75,6 +76,7 @@
     private val trustRepository: TrustRepository,
     @Application private val applicationScope: CoroutineScope,
     private val selectedUserInteractor: SelectedUserInteractor,
+    private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
 ) {
     private val passiveAuthBouncerDelay =
         context.resources.getInteger(R.integer.primary_bouncer_passive_auth_delay).toLong()
@@ -414,15 +416,12 @@
 
     /** Whether we want to wait to show the bouncer in case passive auth succeeds. */
     private fun usePrimaryBouncerPassiveAuthDelay(): Boolean {
-        val canRunFaceAuth =
-            keyguardStateController.isFaceEnrolled &&
-                keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE) &&
-                keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()
         val canRunActiveUnlock =
             currentUserActiveUnlockRunning &&
                 keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState()
 
-        return !needsFullscreenBouncer() && (canRunFaceAuth || canRunActiveUnlock)
+        return !needsFullscreenBouncer() &&
+            (keyguardFaceAuthInteractor.canFaceAuthRun() || canRunActiveUnlock)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index fd7f641..e630fd4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.smartspace.data.repository.SmartspaceRepository
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -46,6 +47,7 @@
     private val widgetRepository: CommunalWidgetRepository,
     mediaRepository: CommunalMediaRepository,
     smartspaceRepository: SmartspaceRepository,
+    keyguardInteractor: KeyguardInteractor,
     private val appWidgetHost: AppWidgetHost,
     private val editWidgetsActivityStarter: EditWidgetsActivityStarter
 ) {
@@ -67,6 +69,8 @@
     val isCommunalShowing: Flow<Boolean> =
         communalRepository.desiredScene.map { it == CommunalSceneKey.Communal }
 
+    val isKeyguardVisible: Flow<Boolean> = keyguardInteractor.isKeyguardVisible
+
     /** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */
     fun onSceneChanged(newScene: CommunalSceneKey) {
         communalRepository.setDesiredScene(newScene)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index b4ab5fb..4d8e893 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -28,6 +28,8 @@
     private val communalInteractor: CommunalInteractor,
     val mediaHost: MediaHost,
 ) {
+    val isKeyguardVisible: Flow<Boolean> = communalInteractor.isKeyguardVisible
+
     val currentScene: StateFlow<CommunalSceneKey> = communalInteractor.desiredScene
 
     fun onSceneChanged(scene: CommunalSceneKey) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index eaf2d57..11bde6b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -39,7 +39,6 @@
     tutorialInteractor: CommunalTutorialInteractor,
     @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
 ) : BaseCommunalViewModel(communalInteractor, mediaHost) {
-
     @OptIn(ExperimentalCoroutinesApi::class)
     override val communalContent: Flow<List<CommunalContentModel>> =
         tutorialInteractor.isTutorialAvailable.flatMapLatest { isTutorialMode ->
diff --git a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
index 31a5d37..4bfc9484 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
@@ -97,7 +97,7 @@
         }
 
     fun canShowFaceScanningAnim(): Boolean {
-        return hasProviders && keyguardUpdateMonitor.isFaceEnrolled
+        return hasProviders && keyguardUpdateMonitor.isFaceEnabledAndEnrolled
     }
 
     fun shouldShowFaceScanningAnim(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 83c16ae..6a0e882 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import javax.inject.Inject
 
 /** A class in which engineers can define flag dependencies */
@@ -26,6 +27,7 @@
 class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, handler: Handler) :
     FlagDependenciesBase(featureFlags, handler) {
     override fun defineDependencies() {
+        NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token
         FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 7d54107..b357b56 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -152,10 +152,6 @@
     @JvmField
     val REFACTOR_GETCURRENTUSER = unreleasedFlag("refactor_getcurrentuser", teamfood = true)
 
-    /** Flag to control the migration of face auth to modern architecture. */
-    // TODO(b/262838215): Tracking bug
-    @JvmField val FACE_AUTH_REFACTOR = releasedFlag("face_auth_refactor")
-
     /** Flag to control the revamp of keyguard biometrics progress animation */
     // TODO(b/244313043): Tracking bug
     @JvmField val BIOMETRICS_ANIMATION_REVAMP = unreleasedFlag("biometrics_animation_revamp")
@@ -464,12 +460,6 @@
     val WALLPAPER_MULTI_CROP =
         sysPropBooleanFlag("persist.wm.debug.wallpaper_multi_crop", default = false)
 
-    // TODO(b/290220798): Tracking Bug
-    @Keep
-    @JvmField
-    val ENABLE_PIP2_IMPLEMENTATION =
-        sysPropBooleanFlag("persist.wm.debug.enable_pip2_implementation", default = false)
-
     // 1200 - predictive back
     @Keep
     @JvmField
@@ -713,7 +703,7 @@
 
     /** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */
     @JvmField
-    val BLUETOOTH_QS_TILE_DIALOG = unreleasedFlag("bluetooth_qs_tile_dialog")
+    val BLUETOOTH_QS_TILE_DIALOG = releasedFlag("bluetooth_qs_tile_dialog")
 
     // TODO(b/300995746): Tracking Bug
     /** A resource flag for whether the communal service is enabled. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index fe9865b..53ec3de 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -3401,7 +3401,7 @@
             }
 
             if (mPowerGestureIntercepted && mOccluded && isSecure()
-                    && mUpdateMonitor.isFaceEnrolled()) {
+                    && mUpdateMonitor.isFaceEnabledAndEnrolled()) {
                 flags |= StatusBarManager.DISABLE_RECENT;
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index c4dfe9a..8ef2662 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -69,7 +69,6 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
@@ -89,7 +88,7 @@
  */
 interface DeviceEntryFaceAuthRepository {
     /** Provide the current face authentication state for device entry. */
-    val isAuthenticated: Flow<Boolean>
+    val isAuthenticated: StateFlow<Boolean>
 
     /** Whether face auth can run at this point. */
     val canRunFaceAuth: StateFlow<Boolean>
@@ -199,8 +198,7 @@
     private val canRunDetection: StateFlow<Boolean>
 
     private val _isAuthenticated = MutableStateFlow(false)
-    override val isAuthenticated: Flow<Boolean>
-        get() = _isAuthenticated
+    override val isAuthenticated: StateFlow<Boolean> = _isAuthenticated
 
     private var cancellationInProgress = MutableStateFlow(false)
 
@@ -243,61 +241,52 @@
                 .collect(Collectors.toSet())
         dumpManager.registerCriticalDumpable("DeviceEntryFaceAuthRepositoryImpl", this)
 
-        if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
-            canRunFaceAuth =
-                listOf(
-                        *gatingConditionsForAuthAndDetect(),
-                        Pair(isLockedOut.isFalse(), "isNotInLockOutState"),
-                        Pair(
-                            trustRepository.isCurrentUserTrusted.isFalse(),
-                            "currentUserIsNotTrusted"
-                        ),
-                        Pair(
-                            biometricSettingsRepository.isFaceAuthCurrentlyAllowed,
-                            "isFaceAuthCurrentlyAllowed"
-                        ),
-                        Pair(isAuthenticated.isFalse(), "faceNotAuthenticated"),
-                    )
-                    .andAllFlows("canFaceAuthRun", faceAuthLog)
-                    .flowOn(mainDispatcher)
-                    .stateIn(applicationScope, SharingStarted.Eagerly, false)
+        canRunFaceAuth =
+            listOf(
+                    *gatingConditionsForAuthAndDetect(),
+                    Pair(isLockedOut.isFalse(), "isNotInLockOutState"),
+                    Pair(trustRepository.isCurrentUserTrusted.isFalse(), "currentUserIsNotTrusted"),
+                    Pair(
+                        biometricSettingsRepository.isFaceAuthCurrentlyAllowed,
+                        "isFaceAuthCurrentlyAllowed"
+                    ),
+                    Pair(isAuthenticated.isFalse(), "faceNotAuthenticated"),
+                )
+                .andAllFlows("canFaceAuthRun", faceAuthLog)
+                .flowOn(mainDispatcher)
+                .stateIn(applicationScope, SharingStarted.Eagerly, false)
 
-            // Face detection can run only when lockscreen bypass is enabled
-            // & detection is supported
-            //   & biometric unlock is not allowed
-            //     or user is trusted by trust manager & we want to run face detect to dismiss
-            // keyguard
-            canRunDetection =
-                listOf(
-                        *gatingConditionsForAuthAndDetect(),
-                        Pair(isBypassEnabled, "isBypassEnabled"),
-                        Pair(
-                            biometricSettingsRepository.isFaceAuthCurrentlyAllowed
-                                .isFalse()
-                                .or(trustRepository.isCurrentUserTrusted),
-                            "faceAuthIsNotCurrentlyAllowedOrCurrentUserIsTrusted"
-                        ),
-                        // We don't want to run face detect if fingerprint can be used to unlock the
-                        // device
-                        // but it's not possible to authenticate with FP from the bouncer (UDFPS)
-                        Pair(
-                            and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning)
-                                .isFalse(),
-                            "udfpsAuthIsNotPossibleAnymore"
-                        )
+        // Face detection can run only when lockscreen bypass is enabled
+        // & detection is supported
+        //   & biometric unlock is not allowed
+        //     or user is trusted by trust manager & we want to run face detect to dismiss
+        // keyguard
+        canRunDetection =
+            listOf(
+                    *gatingConditionsForAuthAndDetect(),
+                    Pair(isBypassEnabled, "isBypassEnabled"),
+                    Pair(
+                        biometricSettingsRepository.isFaceAuthCurrentlyAllowed
+                            .isFalse()
+                            .or(trustRepository.isCurrentUserTrusted),
+                        "faceAuthIsNotCurrentlyAllowedOrCurrentUserIsTrusted"
+                    ),
+                    // We don't want to run face detect if fingerprint can be used to unlock the
+                    // device
+                    // but it's not possible to authenticate with FP from the bouncer (UDFPS)
+                    Pair(
+                        and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning).isFalse(),
+                        "udfpsAuthIsNotPossibleAnymore"
                     )
-                    .andAllFlows("canFaceDetectRun", faceDetectLog)
-                    .flowOn(mainDispatcher)
-                    .stateIn(applicationScope, SharingStarted.Eagerly, false)
-            observeFaceAuthGatingChecks()
-            observeFaceDetectGatingChecks()
-            observeFaceAuthResettingConditions()
-            listenForSchedulingWatchdog()
-            processPendingAuthRequests()
-        } else {
-            canRunFaceAuth = MutableStateFlow(false).asStateFlow()
-            canRunDetection = MutableStateFlow(false).asStateFlow()
-        }
+                )
+                .andAllFlows("canFaceDetectRun", faceDetectLog)
+                .flowOn(mainDispatcher)
+                .stateIn(applicationScope, SharingStarted.Eagerly, false)
+        observeFaceAuthGatingChecks()
+        observeFaceDetectGatingChecks()
+        observeFaceAuthResettingConditions()
+        listenForSchedulingWatchdog()
+        processPendingAuthRequests()
     }
 
     private fun listenForSchedulingWatchdog() {
@@ -454,8 +443,8 @@
                 if (errorStatus.isLockoutError()) {
                     _isLockedOut.value = true
                 }
-                _authenticationStatus.value = errorStatus
                 _isAuthenticated.value = false
+                _authenticationStatus.value = errorStatus
                 if (errorStatus.isHardwareError()) {
                     faceAuthLogger.hardwareError(errorStatus)
                     handleFaceHardwareError()
@@ -477,8 +466,17 @@
             }
 
             override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
-                _authenticationStatus.value = SuccessFaceAuthenticationStatus(result)
+                // Update _isAuthenticated before _authenticationStatus is updated. There are
+                // consumers that receive the face authentication updates through a long chain of
+                // callbacks
+                // _authenticationStatus -> KeyguardUpdateMonitor -> KeyguardStateController ->
+                // onUnlockChanged
+                // These consumers then query the isAuthenticated boolean. This makes sure that the
+                // boolean is updated to new value before the event is propagated.
+                // TODO (b/310592822): once all consumers can use the new system directly, we don't
+                //  have to worry about this ordering.
                 _isAuthenticated.value = true
+                _authenticationStatus.value = SuccessFaceAuthenticationStatus(result)
                 faceAuthLogger.faceAuthSuccess(result)
                 onFaceAuthRequestCompleted()
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
index c8cb9e6..f4a74f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -24,6 +24,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.emptyFlow
 
 /**
@@ -34,11 +35,9 @@
  */
 @SysUISingleton
 class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceAuthRepository {
-    override val isAuthenticated: Flow<Boolean>
-        get() = emptyFlow()
+    override val isAuthenticated: StateFlow<Boolean> = MutableStateFlow(false)
 
-    private val _canRunFaceAuth = MutableStateFlow(false)
-    override val canRunFaceAuth: StateFlow<Boolean> = _canRunFaceAuth
+    override val canRunFaceAuth: StateFlow<Boolean> = MutableStateFlow(false)
 
     override val authenticationStatus: Flow<FaceAuthenticationStatus>
         get() = emptyFlow()
@@ -46,11 +45,9 @@
     override val detectionStatus: Flow<FaceDetectionStatus>
         get() = emptyFlow()
 
-    private val _isLockedOut = MutableStateFlow(false)
-    override val isLockedOut: StateFlow<Boolean> = _isLockedOut
+    override val isLockedOut: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()
 
-    private val _isAuthRunning = MutableStateFlow(false)
-    override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning
+    override val isAuthRunning: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()
 
     override val isBypassEnabled: Flow<Boolean>
         get() = emptyFlow()
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
index 85b0f4fb..5ed70b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
@@ -44,6 +44,8 @@
     /** 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]
@@ -53,9 +55,6 @@
     /** Unregister previously registered listener */
     fun unregisterListener(listener: FaceAuthenticationListener)
 
-    /** Whether the face auth interactor is enabled or not. */
-    fun isEnabled(): Boolean
-
     fun onUdfpsSensorTouched()
     fun onAssistantTriggeredOnLockScreen()
     fun onDeviceLifted()
@@ -65,6 +64,9 @@
     fun onPrimaryBouncerUserInput()
     fun onAccessibilityAction()
     fun onWalletLaunched()
+
+    /** Whether face auth is considered class 3 */
+    fun isFaceAuthStrong(): Boolean
 }
 
 /**
@@ -81,4 +83,10 @@
 
     /** 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/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 949c940..91b6715 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -31,8 +31,6 @@
 import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
@@ -76,8 +74,7 @@
 constructor(
     private val repository: KeyguardRepository,
     private val commandQueue: CommandQueue,
-    private val powerInteractor: PowerInteractor,
-    featureFlags: FeatureFlags,
+    powerInteractor: PowerInteractor,
     sceneContainerFlags: SceneContainerFlags,
     bouncerRepository: KeyguardBouncerRepository,
     configurationRepository: ConfigurationRepository,
@@ -197,22 +194,18 @@
 
     /** Whether camera is launched over keyguard. */
     val isSecureCameraActive: Flow<Boolean> by lazy {
-        if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
-            combine(
-                    isKeyguardVisible,
-                    primaryBouncerShowing,
-                    onCameraLaunchDetected,
-                ) { isKeyguardVisible, isPrimaryBouncerShowing, cameraLaunchEvent ->
-                    when {
-                        isKeyguardVisible -> false
-                        isPrimaryBouncerShowing -> false
-                        else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP
-                    }
+        combine(
+                isKeyguardVisible,
+                primaryBouncerShowing,
+                onCameraLaunchDetected,
+            ) { isKeyguardVisible, isPrimaryBouncerShowing, cameraLaunchEvent ->
+                when {
+                    isKeyguardVisible -> false
+                    isPrimaryBouncerShowing -> false
+                    else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP
                 }
-                .onStart { emit(false) }
-        } else {
-            flowOf(false)
-        }
+            }
+            .onStart { emit(false) }
     }
 
     /** The approximate location on the screen of the fingerprint sensor, if one is available. */
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
index fbadde6..cd6ab31 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
@@ -42,9 +42,12 @@
 
     override fun isLockedOut(): Boolean = false
 
-    override fun isEnabled() = 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) {}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index 2641846..ae356cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -30,8 +30,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
-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.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
@@ -45,6 +43,7 @@
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.sample
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -71,10 +70,9 @@
     @Application private val applicationScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
     private val repository: DeviceEntryFaceAuthRepository,
-    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+    private val primaryBouncerInteractor: Lazy<PrimaryBouncerInteractor>,
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
-    private val featureFlags: FeatureFlags,
     private val faceAuthenticationLogger: FaceAuthenticationLogger,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
@@ -88,16 +86,16 @@
     private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
 
     override fun start() {
-        if (!isEnabled()) {
-            return
-        }
-        // This is required because fingerprint state required for the face auth repository is
-        // backed by KeyguardUpdateMonitor. KeyguardUpdateMonitor constructor accesses the biometric
-        // state which makes lazy injection not an option.
+        // Todo(b/310594096): there is a dependency cycle introduced by the repository depending on
+        //  KeyguardBypassController, which in turn depends on KeyguardUpdateMonitor through
+        //  its other dependencies. Once bypassEnabled state is available through a repository, we
+        //  can break that cycle and inject this interactor directly into KeyguardUpdateMonitor
         keyguardUpdateMonitor.setFaceAuthInteractor(this)
         observeFaceAuthStateUpdates()
         faceAuthenticationLogger.interactorStarted()
-        primaryBouncerInteractor.isShowing
+        primaryBouncerInteractor
+            .get()
+            .isShowing
             .whenItFlipsToTrue()
             .onEach {
                 faceAuthenticationLogger.bouncerVisibilityChanged()
@@ -176,7 +174,7 @@
                         FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
                         // Fallback to detection if bouncer is not showing so that we can detect a
                         // face and then show the bouncer to the user if face auth can't run
-                        fallbackToDetect = !primaryBouncerInteractor.isBouncerShowing()
+                        fallbackToDetect = !primaryBouncerInteractor.get().isBouncerShowing()
                     )
                 }
             }
@@ -231,9 +229,8 @@
 
     override fun canFaceAuthRun(): Boolean = repository.canRunFaceAuth.value
 
-    override fun isEnabled(): Boolean {
-        return featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)
-    }
+    override fun isFaceAuthStrong(): Boolean =
+        facePropertyRepository.sensorInfo.value?.strength == SensorStrength.STRONG
 
     override fun onPrimaryBouncerUserInput() {
         repository.cancel()
@@ -248,29 +245,24 @@
     override val detectionStatus = repository.detectionStatus
 
     private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) {
-        if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
-            if (repository.isLockedOut.value) {
-                faceAuthenticationStatusOverride.value =
-                    ErrorFaceAuthenticationStatus(
-                        BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT,
-                        context.resources.getString(R.string.keyguard_face_unlock_unavailable)
-                    )
-            } else {
-                faceAuthenticationStatusOverride.value = null
-                faceAuthenticationLogger.authRequested(uiEvent)
-                repository.requestAuthenticate(uiEvent, fallbackToDetection = fallbackToDetect)
-            }
+        if (repository.isLockedOut.value) {
+            faceAuthenticationStatusOverride.value =
+                ErrorFaceAuthenticationStatus(
+                    BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT,
+                    context.resources.getString(R.string.keyguard_face_unlock_unavailable)
+                )
         } else {
-            faceAuthenticationLogger.ignoredFaceAuthTrigger(
-                uiEvent,
-                ignoredReason = "Skipping face auth request because feature flag is false"
-            )
+            faceAuthenticationStatusOverride.value = null
+            faceAuthenticationLogger.authRequested(uiEvent)
+            repository.requestAuthenticate(uiEvent, fallbackToDetection = fallbackToDetect)
         }
     }
 
     override fun isFaceAuthEnabledAndEnrolled(): Boolean =
         biometricSettingsRepository.isFaceAuthEnrolledAndEnabled.value
 
+    override fun isAuthenticated(): Boolean = repository.isAuthenticated.value
+
     private fun observeFaceAuthStateUpdates() {
         authenticationStatus
             .onEach { authStatusUpdate ->
@@ -284,6 +276,21 @@
             }
             .flowOn(mainDispatcher)
             .launchIn(applicationScope)
+        repository.isLockedOut
+            .onEach { lockedOut -> listeners.forEach { it.onLockoutStateChanged(lockedOut) } }
+            .flowOn(mainDispatcher)
+            .launchIn(applicationScope)
+        repository.isAuthRunning
+            .onEach { running -> listeners.forEach { it.onRunningStateChanged(running) } }
+            .flowOn(mainDispatcher)
+            .launchIn(applicationScope)
+
+        biometricSettingsRepository.isFaceAuthEnrolledAndEnabled
+            .onEach { enrolledAndEnabled ->
+                listeners.forEach { it.onAuthEnrollmentStateChanged(enrolledAndEnabled) }
+            }
+            .flowOn(mainDispatcher)
+            .launchIn(applicationScope)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 99529a1..9a50d83 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -31,6 +31,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 
@@ -49,12 +50,22 @@
         transitionInteractor.startedKeyguardState.map { keyguardState ->
             keyguardState == KeyguardState.AOD
         }
+
+    private fun getColor(usingBackgroundProtection: Boolean): Int {
+        return if (usingBackgroundProtection) {
+            Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
+        } else {
+            Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
+        }
+    }
+
     private val color: Flow<Int> =
-        configurationRepository.onAnyConfigurationChange
-            .map { Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) }
-            .onStart {
-                emit(Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary))
-            }
+        deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBgProtection ->
+            configurationRepository.onAnyConfigurationChange
+                .map { getColor(useBgProtection) }
+                .onStart { emit(getColor(useBgProtection)) }
+        }
+
     private val useAodIconVariant: Flow<Boolean> =
         combine(isShowingAod, deviceEntryUdfpsInteractor.isUdfpsSupported) {
                 isTransitionToAod,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 315626b..b3c7d379 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -67,7 +66,6 @@
     private val context: Context,
     private val deviceEntryInteractor: DeviceEntryInteractor,
     private val dozeParameters: DozeParameters,
-    private val featureFlags: FeatureFlagsClassic,
     private val keyguardInteractor: KeyguardInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index f2db088..44232ff 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -19,12 +19,18 @@
 import android.app.StatusBarManager
 import android.os.UserHandle
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
 import javax.inject.Inject
 
 @SysUISingleton
-class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) {
+class MediaFlags
+@Inject
+constructor(
+    private val featureFlags: FeatureFlagsClassic,
+    private val sceneContainerFlags: SceneContainerFlags
+) {
     /**
      * Check whether media control actions should be based on PlaybackState instead of notification
      */
@@ -48,4 +54,8 @@
 
     /** Check whether we allow remote media to generate resume controls */
     fun isRemoteResumeAllowed() = featureFlags.isEnabled(Flags.MEDIA_REMOTE_RESUME)
+
+    /** Check whether to use flexiglass layout */
+    fun isFlexiglassEnabled() =
+        sceneContainerFlags.isEnabled() && MediaInSceneContainerFlag.isEnabled
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt
similarity index 73%
copy from packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt
copy to packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt
index 44387c2..898298c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt
@@ -14,26 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification.shared
+package com.android.systemui.media.controls.util
 
 import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
 import com.android.systemui.flags.RefactorFlagUtils
 
-/** Helper for reading or using the notifications live data store refactor flag state. */
+/** Helper for reading or using the media_in_scene_container flag state. */
 @Suppress("NOTHING_TO_INLINE")
-object NotificationsLiveDataStoreRefactor {
+object MediaInSceneContainerFlag {
     /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_LIVE_DATA_STORE_REFACTOR
+    const val FLAG_NAME = Flags.FLAG_MEDIA_IN_SCENE_CONTAINER
 
-    /** A token used for dependency declaration */
-    val token: FlagToken
-        get() = FlagToken(FLAG_NAME, isEnabled)
-
-    /** Is the refactor enabled */
+    /** Is the flag enabled? */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.notificationsLiveDataStoreRefactor()
+        get() = Flags.mediaInSceneContainer()
 
     /**
      * Called to ensure code is only run when the flag is enabled. This protects users from the
@@ -50,4 +45,4 @@
      */
     @JvmStatic
     inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 58c4f0d..ef72967 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -131,6 +131,9 @@
                     + "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})"
                     + "?)*";
 
+    // Not all JDKs support emoji patterns, including the one errorprone runs under, which
+    // makes it think that this is an invalid pattern.
+    @SuppressWarnings("InvalidPatternSyntax")
     static final Pattern EMOJI_PATTERN = Pattern.compile(UNICODE_EMOJI_REGEX);
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
index 346d5c3..e5e1e84 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.qs.ui.viewmodel
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.scene.shared.model.Direction
 import com.android.systemui.scene.shared.model.SceneKey
@@ -33,14 +32,10 @@
 class QuickSettingsSceneViewModel
 @Inject
 constructor(
-    private val deviceEntryInteractor: DeviceEntryInteractor,
     val shadeHeaderViewModel: ShadeHeaderViewModel,
     val qsSceneAdapter: QSSceneAdapter,
     val notifications: NotificationsPlaceholderViewModel,
 ) {
-    /** Notifies that some content in quick settings was clicked. */
-    fun onContentClicked() = deviceEntryInteractor.attemptDeviceEntry()
-
     val destinationScenes =
         qsSceneAdapter.isCustomizing.map { customizing ->
             if (customizing) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index c810786..c06e9a4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -98,7 +98,6 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.ActiveUnlockConfig;
-import com.android.keyguard.FaceAuthApiRequestReason;
 import com.android.keyguard.KeyguardClockSwitch.ClockSize;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardStatusViewController;
@@ -2966,10 +2965,8 @@
                     // Try triggering face auth, this "might" run. Check
                     // KeyguardUpdateMonitor#shouldListenForFace to see when face auth won't run.
                     mKeyguardFaceAuthInteractor.onNotificationPanelClicked();
-                    boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth(
-                            FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED);
 
-                    if (didFaceAuthRun) {
+                    if (mKeyguardFaceAuthInteractor.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 e84bfc5..dd194eaa 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -61,7 +61,6 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.policy.SystemBarUtils;
-import com.android.keyguard.FaceAuthApiRequestReason;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
@@ -981,7 +980,6 @@
         // this will speed up notification actions.
         if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) {
             mKeyguardFaceAuthInteractor.onQsExpansionStared();
-            mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.QS_EXPANDED);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index dd24ca7..08415cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -1033,7 +1033,7 @@
         if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
             if (mAlternateBouncerInteractor.isVisibleState()) {
                 return; // udfps affordance is highlighted, no need to show action to unlock
-            } else if (mKeyguardUpdateMonitor.isFaceEnrolled()
+            } else if (mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled()
                     && !mKeyguardUpdateMonitor.getIsFaceAuthenticated()) {
                 String message;
                 if (mAccessibilityManager.isEnabled()
@@ -1215,7 +1215,7 @@
                             mContext.getString(R.string.keyguard_suggest_fingerprint)
                     );
                 } else if (fpAuthFailed
-                        && mKeyguardUpdateMonitor.getUserUnlockedWithFace(getCurrentUser())) {
+                        && mKeyguardUpdateMonitor.isCurrentUserUnlockedWithFace()) {
                     // face had already previously unlocked the device, so instead of showing a
                     // fingerprint error, tell them they have already unlocked with face auth
                     // and how to enter their device
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt
index 747efe3..933d0ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt
@@ -36,7 +36,8 @@
     /**
      * A mode where notification icons in the status bar are hidden and replaced by a dot (this mode
      * can be requested by apps). See
-     * [com.android.systemui.statusbar.phone.LightsOutNotifController].
+     * [com.android.systemui.statusbar.phone.LegacyLightsOutNotifController] and
+     * [com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor].
      */
     LIGHTS_OUT,
     /** Similar to [LIGHTS_OUT], but also with a transparent background for the status bar. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt
index d1594ef..0415212 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt
@@ -33,7 +33,7 @@
 
 /**
  * Repository for data that's specific to the status bar **on keyguard**. For data that applies to
- * all status bars, use [StatusBarModeRepository].
+ * all status bars, use [StatusBarModeRepositoryStore].
  */
 interface KeyguardStatusBarRepository {
     /** True if we can show the user switcher on keyguard and false otherwise. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index 47994d9..6429815 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -25,10 +25,8 @@
 import android.view.WindowInsetsController.Appearance
 import com.android.internal.statusbar.LetterboxDetails
 import com.android.internal.view.AppearanceRegion
-import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.Dumpable
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.DisplayId
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
 import com.android.systemui.statusbar.data.model.StatusBarAppearance
@@ -38,13 +36,10 @@
 import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
-import dagger.multibindings.IntoSet
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import java.io.PrintWriter
-import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -61,7 +56,7 @@
  * Note: These status bar modes are status bar *window* states that are sent to us from
  * WindowManager, not determined internally.
  */
-interface StatusBarModeRepository {
+interface StatusBarModePerDisplayRepository {
     /**
      * True if the status bar window is showing transiently and will disappear soon, and false
      * otherwise. ("Otherwise" in this case means the status bar is persistently hidden OR
@@ -108,16 +103,15 @@
     fun clearTransient()
 }
 
-@SysUISingleton
-class StatusBarModeRepositoryImpl
-@Inject
+class StatusBarModePerDisplayRepositoryImpl
+@AssistedInject
 constructor(
     @Application scope: CoroutineScope,
-    @DisplayId thisDisplayId: Int,
+    @Assisted("displayId") thisDisplayId: Int,
     private val commandQueue: CommandQueue,
     private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator,
     ongoingCallRepository: OngoingCallRepository,
-) : StatusBarModeRepository, CoreStartable, OnStatusBarViewInitializedListener {
+) : StatusBarModePerDisplayRepository, OnStatusBarViewInitializedListener, Dumpable {
 
     private val commandQueueCallback =
         object : CommandQueue.Callbacks {
@@ -166,7 +160,7 @@
             }
         }
 
-    override fun start() {
+    fun start() {
         commandQueue.addCallback(commandQueueCallback)
     }
 
@@ -340,16 +334,7 @@
     )
 }
 
-@Module
-interface StatusBarModeRepositoryModule {
-    @Binds fun bindImpl(impl: StatusBarModeRepositoryImpl): StatusBarModeRepository
-
-    @Binds
-    @IntoMap
-    @ClassKey(StatusBarModeRepositoryImpl::class)
-    fun bindCoreStartable(impl: StatusBarModeRepositoryImpl): CoreStartable
-
-    @Binds
-    @IntoSet
-    fun bindViewInitListener(impl: StatusBarModeRepositoryImpl): OnStatusBarViewInitializedListener
+@AssistedFactory
+interface StatusBarModePerDisplayRepositoryFactory {
+    fun create(@Assisted("displayId") displayId: Int): StatusBarModePerDisplayRepositoryImpl
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
new file mode 100644
index 0000000..962cb095
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.DisplayId
+import com.android.systemui.statusbar.core.StatusBarInitializer
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
+import java.io.PrintWriter
+import javax.inject.Inject
+
+interface StatusBarModeRepositoryStore {
+    val defaultDisplay: StatusBarModePerDisplayRepository
+    fun forDisplay(displayId: Int): StatusBarModePerDisplayRepository
+}
+
+@SysUISingleton
+class StatusBarModeRepositoryImpl
+@Inject
+constructor(
+    @DisplayId private val displayId: Int,
+    factory: StatusBarModePerDisplayRepositoryFactory
+) :
+    StatusBarModeRepositoryStore,
+    CoreStartable,
+    StatusBarInitializer.OnStatusBarViewInitializedListener {
+    override val defaultDisplay = factory.create(displayId)
+
+    override fun forDisplay(displayId: Int) =
+        if (this.displayId == displayId) {
+            defaultDisplay
+        } else {
+            TODO("b/127878649 implement multi-display state management")
+        }
+
+    override fun start() {
+        defaultDisplay.start()
+    }
+
+    override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) {
+        defaultDisplay.onStatusBarViewInitialized(component)
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        defaultDisplay.dump(pw, args)
+    }
+}
+
+@Module
+interface StatusBarModeRepositoryModule {
+    @Binds fun bindImpl(impl: StatusBarModeRepositoryImpl): StatusBarModeRepositoryStore
+
+    @Binds
+    @IntoMap
+    @ClassKey(StatusBarModeRepositoryStore::class)
+    fun bindCoreStartable(impl: StatusBarModeRepositoryImpl): CoreStartable
+
+    @Binds
+    @IntoSet
+    fun bindViewInitListener(
+        impl: StatusBarModeRepositoryImpl
+    ): StatusBarInitializer.OnStatusBarViewInitializedListener
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 11c65e5..6cb079a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2221,8 +2221,8 @@
                     if (mMenuRow != null) {
                         mMenuRow.resetMenu();
                     }
-                    mTranslateAnim = null;
                 }
+                mTranslateAnim = null;
             }
         });
         mTranslateAnim = translateAnim;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 8eda96f..64f61d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -27,6 +27,7 @@
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 
 import androidx.annotation.NonNull;
@@ -42,9 +43,11 @@
  * A view that can be used for both the dimmed and normal background of an notification.
  */
 public class NotificationBackgroundView extends View implements Dumpable {
+    private static final String TAG = "NotificationBackgroundView";
 
     private final boolean mDontModifyCorners;
     private Drawable mBackground;
+    private Drawable mBackgroundDrawableToTint;
     private int mClipTopAmount;
     private int mClipBottomAmount;
     private int mTintColor;
@@ -131,6 +134,7 @@
             unscheduleDrawable(mBackground);
         }
         mBackground = background;
+        mBackgroundDrawableToTint = findBackgroundDrawableToTint(mBackground);
         mRippleColor = null;
         mBackground.mutate();
         if (mBackground != null) {
@@ -144,25 +148,46 @@
         invalidate();
     }
 
+    // setCustomBackground should be called from ActivatableNotificationView.initBackground
+    // with R.drawable.notification_material_bg, which is a layer-list with a lower layer
+    // for the background color (annotated with an ID so we can find it) and an upper layer
+    // to blend in the stateful @color/notification_overlay_color.
+    //
+    // If the notification is tinted, we want to set a tint list on *just that lower layer* that
+    // will replace the default materialColorSurfaceContainerHigh *without* wiping out the stateful
+    // tints in the upper layer that make the hovered and pressed states visible.
+    //
+    // This function fishes that lower layer out, or makes a fuss in logcat if it can't find it.
+    private @Nullable Drawable findBackgroundDrawableToTint(@Nullable Drawable background) {
+        if (background == null) {
+            return null;
+        }
+
+        if (!(background instanceof LayerDrawable)) {
+            Log.wtf(TAG, "background is not a LayerDrawable: " + background);
+            return background;
+        }
+
+        final Drawable backgroundColorLayer = ((LayerDrawable) background).findDrawableByLayerId(
+                R.id.notification_background_color_layer);
+
+        if (backgroundColorLayer == null) {
+            Log.wtf(TAG, "background is missing background color layer: " + background);
+            return background;
+        }
+
+        return backgroundColorLayer;
+    }
+
     public void setCustomBackground(int drawableResId) {
         final Drawable d = mContext.getDrawable(drawableResId);
         setCustomBackground(d);
     }
 
     public void setTint(int tintColor) {
-        if (tintColor != 0) {
-            ColorStateList stateList = new ColorStateList(new int[][]{
-                    new int[]{com.android.internal.R.attr.state_pressed},
-                    new int[]{com.android.internal.R.attr.state_hovered},
-                    new int[]{}},
+        mBackgroundDrawableToTint.setTint(tintColor);
+        mBackgroundDrawableToTint.setTintMode(PorterDuff.Mode.SRC_ATOP);
 
-                    new int[]{tintColor, tintColor, tintColor}
-            );
-            mBackground.setTintMode(PorterDuff.Mode.SRC_ATOP);
-            mBackground.setTintList(stateList);
-        } else {
-            mBackground.setTintList(null);
-        }
         mTintColor = tintColor;
         invalidate();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataStoreRefactor.kt
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataStoreRefactor.kt
index 44387c2..8fc7106 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataStoreRefactor.kt
@@ -50,4 +50,4 @@
      */
     @JvmStatic
     inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 283a593..6944453 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -565,6 +565,7 @@
     private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
     private final ScreenOffAnimationController mScreenOffAnimationController;
     private boolean mShouldUseSplitNotificationShade;
+    private boolean mShouldSkipTopPaddingAnimationAfterFold = false;
     private boolean mHasFilteredOutSeenNotifications;
     @Nullable private SplitShadeStateController mSplitShadeStateController = null;
     private boolean mIsSmallLandscapeLockscreenEnabled = false;
@@ -1364,7 +1365,11 @@
             mTopPadding = topPadding;
             updateAlgorithmHeightAndPadding();
             updateContentHeight();
-            if (shouldAnimate && mAnimationsEnabled && mIsExpanded) {
+            if (mAmbientState.isOnKeyguard()
+                    && !mShouldUseSplitNotificationShade
+                    && mShouldSkipTopPaddingAnimationAfterFold) {
+                mShouldSkipTopPaddingAnimationAfterFold = false;
+            } else if (shouldAnimate && mAnimationsEnabled && mIsExpanded) {
                 mTopPaddingNeedsAnimation = true;
                 mNeedsAnimation = true;
             }
@@ -3751,6 +3756,11 @@
     }
 
     protected boolean isInsideQsHeader(MotionEvent ev) {
+        if (mQsHeader == null) {
+            Log.wtf(TAG, "qsHeader is null while NSSL is handling a touch");
+            return false;
+        }
+
         mQsHeader.getBoundsOnScreen(mQsHeaderBound);
         /**
          * One-handed mode defines a feature FEATURE_ONE_HANDED of DisplayArea {@link DisplayArea}
@@ -5736,6 +5746,7 @@
         boolean split = mSplitShadeStateController.shouldUseSplitNotificationShade(getResources());
         if (split != mShouldUseSplitNotificationShade) {
             mShouldUseSplitNotificationShade = split;
+            mShouldSkipTopPaddingAnimationAfterFold = true;
             mAmbientState.setUseSplitShade(split);
             updateDismissBehavior();
             updateUseRoundedRectClipping();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index e1fba2e..7aa7976 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -372,26 +372,6 @@
         )
     }
 
-    override fun executeRunnableDismissingKeyguard(
-        runnable: Runnable?,
-        cancelAction: Runnable?,
-        dismissShade: Boolean,
-        afterKeyguardGone: Boolean,
-        deferred: Boolean,
-        willAnimateOnKeyguard: Boolean,
-        customMessage: String?,
-    ) {
-        activityStarterInternal.executeRunnableDismissingKeyguard(
-            runnable = runnable,
-            cancelAction = cancelAction,
-            dismissShade = dismissShade,
-            afterKeyguardGone = afterKeyguardGone,
-            deferred = deferred,
-            willAnimateOnKeyguard = willAnimateOnKeyguard,
-            customMessage = customMessage,
-        )
-    }
-
     override fun postQSRunnableDismissingKeyguard(runnable: Runnable?) {
         postOnUiThread {
             statusBarStateController.setLeaveOpenOnKeyguardHide(true)
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 645769c..57d49b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -33,7 +33,6 @@
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.IWallpaperManager;
 import android.app.KeyguardManager;
@@ -200,7 +199,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.core.StatusBarInitializer;
 import com.android.systemui.statusbar.data.model.StatusBarMode;
-import com.android.systemui.statusbar.data.repository.StatusBarModeRepository;
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -388,7 +387,7 @@
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final StatusBarInitializer mStatusBarInitializer;
     private final StatusBarWindowController mStatusBarWindowController;
-    private final StatusBarModeRepository mStatusBarModeRepository;
+    private final StatusBarModeRepositoryStore mStatusBarModeRepository;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @VisibleForTesting
     DozeServiceHost mDozeServiceHost;
@@ -606,7 +605,7 @@
             StatusBarInitializer statusBarInitializer,
             StatusBarWindowController statusBarWindowController,
             StatusBarWindowStateController statusBarWindowStateController,
-            StatusBarModeRepository statusBarModeRepository,
+            StatusBarModeRepositoryStore statusBarModeRepository,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             StatusBarSignalPolicy statusBarSignalPolicy,
             PulseExpansionHandler pulseExpansionHandler,
@@ -900,7 +899,7 @@
         setUpPresenter();
 
         if ((result.mTransientBarTypes & WindowInsets.Type.statusBars()) != 0) {
-            mStatusBarModeRepository.showTransient();
+            mStatusBarModeRepository.getDefaultDisplay().showTransient();
         }
         mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance,
                 result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior,
@@ -1147,9 +1146,10 @@
 
         mDemoModeController.addCallback(mDemoModeCallback);
         mJavaAdapter.alwaysCollectFlow(
-                mStatusBarModeRepository.isTransientShown(), this::onTransientShownChanged);
+                mStatusBarModeRepository.getDefaultDisplay().isTransientShown(),
+                this::onTransientShownChanged);
         mJavaAdapter.alwaysCollectFlow(
-                mStatusBarModeRepository.getStatusBarMode(),
+                mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode(),
                 this::updateBarMode);
 
         mCommandQueueCallbacks = mCommandQueueCallbacksLazy.get();
@@ -1209,7 +1209,7 @@
 
             @Override
             public void hide() {
-                mStatusBarModeRepository.clearTransient();
+                mStatusBarModeRepository.getDefaultDisplay().clearTransient();
             }
         });
 
@@ -1657,7 +1657,7 @@
         if (mDemoModeController.isInDemoMode()) return;
         if (mStatusBarTransitions != null) {
             checkBarMode(
-                    mStatusBarModeRepository.getStatusBarMode().getValue(),
+                    mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode().getValue(),
                     mStatusBarWindowState,
                     mStatusBarTransitions);
         }
@@ -1668,7 +1668,8 @@
     /** Temporarily hides Bubbles if the status bar is hidden. */
     @Override
     public void updateBubblesVisibility() {
-        StatusBarMode mode = mStatusBarModeRepository.getStatusBarMode().getValue();
+        StatusBarMode mode =
+                mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode().getValue();
         mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged(
                 mode != StatusBarMode.LIGHTS_OUT
                         && mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT
@@ -2927,45 +2928,6 @@
         }
     }
 
-    /**
-     * Dismiss the keyguard then execute an action.
-     *
-     * @param action The action to execute after dismissing the keyguard.
-     * @param collapsePanel Whether we should collapse the panel after dismissing the keyguard.
-     * @param willAnimateOnKeyguard Whether {@param action} will run an animation on the keyguard if
-     *                              we are locked.
-     */
-    private void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone,
-            boolean collapsePanel, boolean willAnimateOnKeyguard) {
-        if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
-
-        OnDismissAction onDismissAction = new OnDismissAction() {
-            @Override
-            public boolean onDismiss() {
-                new Thread(() -> {
-                    try {
-                        // The intent we are sending is for the application, which
-                        // won't have permission to immediately start an activity after
-                        // the user switches to home.  We know it is safe to do at this
-                        // point, so make sure new activity switches are now allowed.
-                        ActivityManager.getService().resumeAppSwitches();
-                    } catch (RemoteException e) {
-                    }
-                    action.run();
-                }).start();
-
-                return collapsePanel ? mShadeController.collapseShade() : willAnimateOnKeyguard;
-            }
-
-            @Override
-            public boolean willRunAnimationOnKeyguard() {
-                return willAnimateOnKeyguard;
-            }
-        };
-        mActivityStarter.dismissKeyguardThenExecute(onDismissAction, /* cancel= */ null,
-                afterKeyguardGone);
-    }
-
     private void clearNotificationEffects() {
         try {
             mBarService.clearNotificationEffects();
@@ -2993,7 +2955,7 @@
     // End Extra BaseStatusBarMethods.
 
     boolean isTransientShown() {
-        return mStatusBarModeRepository.isTransientShown().getValue();
+        return mStatusBarModeRepository.getDefaultDisplay().isTransientShown().getValue();
     }
 
     private void updateLightRevealScrimVisibility() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index b0183d3..674f169 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -98,7 +98,7 @@
                 FACE_UNLOCK_BYPASS_NEVER -> false
                 else -> field
             }
-            return enabled && mKeyguardStateController.isFaceEnrolled &&
+            return enabled && mKeyguardStateController.isFaceEnrolledAndEnabled &&
                     isPostureAllowedForFaceAuth()
         }
         private set(value) {
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 3329844..32b3ac2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -22,7 +22,6 @@
 import android.hardware.TriggerEvent
 import android.hardware.TriggerEventListener
 import com.android.keyguard.ActiveUnlockConfig
-import com.android.keyguard.FaceAuthApiRequestReason
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.CoreStartable
@@ -77,9 +76,6 @@
             isListening = false
             updateListeningState()
             keyguardFaceAuthInteractor.onDeviceLifted()
-            keyguardUpdateMonitor.requestFaceAuth(
-                FaceAuthApiRequestReason.PICK_UP_GESTURE_TRIGGERED
-            )
             keyguardUpdateMonitor.requestActiveUnlock(
                 ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE,
                 "KeyguardLiftController")
@@ -117,7 +113,8 @@
         val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
                 !statusBarStateController.isDozing
 
-        val shouldListen = (onKeyguard || bouncerVisible) && keyguardUpdateMonitor.isFaceEnrolled
+        val isFaceEnabled = keyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
+        val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled
         if (shouldListen != isListening) {
             isListening = shouldListen
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java
index eba7fe0..7c871e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java
@@ -36,6 +36,7 @@
 import com.android.internal.view.AppearanceRegion;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
 import com.android.systemui.util.ViewController;
 
@@ -51,7 +52,7 @@
  * whether there are notifications when the device is in {@link View#SYSTEM_UI_FLAG_LOW_PROFILE}.
  */
 @StatusBarFragmentScope
-public class LightsOutNotifController extends ViewController<View> {
+public class LegacyLightsOutNotifController extends ViewController<View> {
     private final CommandQueue mCommandQueue;
     private final NotifLiveDataStore mNotifDataStore;
     private final WindowManager mWindowManager;
@@ -63,7 +64,7 @@
     private int mDisplayId;
 
     @Inject
-    LightsOutNotifController(
+    LegacyLightsOutNotifController(
             @Named(LIGHTS_OUT_NOTIF_VIEW) View lightsOutNotifView,
             WindowManager windowManager,
             NotifLiveDataStore notifDataStore,
@@ -72,7 +73,12 @@
         mWindowManager = windowManager;
         mNotifDataStore = notifDataStore;
         mCommandQueue = commandQueue;
+    }
 
+    @Override
+    protected void onInit() {
+        super.onInit();
+        NotificationsLiveDataStoreRefactor.assertInLegacyMode();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 4d3e2ad..eec617b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -42,7 +42,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.statusbar.data.model.StatusBarAppearance;
-import com.android.systemui.statusbar.data.repository.StatusBarModeRepository;
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.kotlin.JavaAdapter;
@@ -68,7 +68,7 @@
     private final JavaAdapter mJavaAdapter;
     private final SysuiDarkIconDispatcher mStatusBarIconController;
     private final BatteryController mBatteryController;
-    private final StatusBarModeRepository mStatusBarModeRepository;
+    private final StatusBarModeRepositoryStore mStatusBarModeRepository;
     private BiometricUnlockController mBiometricUnlockController;
 
     private LightBarTransitionsController mNavigationBarController;
@@ -126,7 +126,7 @@
             DarkIconDispatcher darkIconDispatcher,
             BatteryController batteryController,
             NavigationModeController navModeController,
-            StatusBarModeRepository statusBarModeRepository,
+            StatusBarModeRepositoryStore statusBarModeRepository,
             DumpManager dumpManager,
             DisplayTracker displayTracker) {
         mJavaAdapter = javaAdapter;
@@ -146,7 +146,7 @@
     @Override
     public void start() {
         mJavaAdapter.alwaysCollectFlow(
-                mStatusBarModeRepository.getStatusBarAppearance(),
+                mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance(),
                 this::onStatusBarAppearanceChanged);
     }
 
@@ -476,7 +476,7 @@
         private final DarkIconDispatcher mDarkIconDispatcher;
         private final BatteryController mBatteryController;
         private final NavigationModeController mNavModeController;
-        private final StatusBarModeRepository mStatusBarModeRepository;
+        private final StatusBarModeRepositoryStore mStatusBarModeRepository;
         private final DumpManager mDumpManager;
         private final DisplayTracker mDisplayTracker;
 
@@ -486,7 +486,7 @@
                 DarkIconDispatcher darkIconDispatcher,
                 BatteryController batteryController,
                 NavigationModeController navModeController,
-                StatusBarModeRepository statusBarModeRepository,
+                StatusBarModeRepositoryStore statusBarModeRepository,
                 DumpManager dumpManager,
                 DisplayTracker displayTracker) {
             mJavaAdapter = javaAdapter;
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 274b50f..daadedb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1636,13 +1636,6 @@
     }
 
     /**
-     * Request to authenticate using face.
-     */
-    public void requestFace(boolean request) {
-        mKeyguardUpdateManager.requestFaceAuthOnOccludingApp(request);
-    }
-
-    /**
      * Request to authenticate using the fingerprint sensor.  If the fingerprint sensor is udfps,
      * uses the color provided by udfpsColor for the fingerprint icon.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt
new file mode 100644
index 0000000..ed8b3e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/**
+ * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where status
+ * bar and navigation icons dim. In this mode, a notification dot appears where the notification
+ * icons would appear if they would be shown outside of this mode.
+ *
+ * This interactor knows whether the device is in [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE].
+ */
+@SysUISingleton
+class LightsOutInteractor
+@Inject
+constructor(private val repository: StatusBarModeRepositoryStore) {
+
+    fun isLowProfile(displayId: Int): Flow<Boolean> =
+        repository.forDisplay(displayId).statusBarMode.map {
+            when (it) {
+                StatusBarMode.LIGHTS_OUT,
+                StatusBarMode.LIGHTS_OUT_TRANSPARENT -> true
+                else -> false
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 7adc08c..49880d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -43,7 +43,6 @@
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlagsClassic;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
 import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -139,7 +138,6 @@
     private final OngoingCallController mOngoingCallController;
     private final SystemStatusAnimationScheduler mAnimationScheduler;
     private final StatusBarLocationPublisher mLocationPublisher;
-    private final FeatureFlagsClassic mFeatureFlags;
     private final NotificationIconAreaController mNotificationIconAreaController;
     private final ShadeExpansionStateManager mShadeExpansionStateManager;
     private final StatusBarIconController mStatusBarIconController;
@@ -228,7 +226,6 @@
             StatusBarLocationPublisher locationPublisher,
             NotificationIconAreaController notificationIconAreaController,
             ShadeExpansionStateManager shadeExpansionStateManager,
-            FeatureFlagsClassic featureFlags,
             StatusBarIconController statusBarIconController,
             DarkIconManager.Factory darkIconManagerFactory,
             CollapsedStatusBarViewModel collapsedStatusBarViewModel,
@@ -258,7 +255,6 @@
         mLocationPublisher = locationPublisher;
         mNotificationIconAreaController = notificationIconAreaController;
         mShadeExpansionStateManager = shadeExpansionStateManager;
-        mFeatureFlags = featureFlags;
         mStatusBarIconController = statusBarIconController;
         mCollapsedStatusBarViewModel = collapsedStatusBarViewModel;
         mCollapsedStatusBarViewBinder = collapsedStatusBarViewBinder;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index 0618abb..96faa35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -18,8 +18,9 @@
 
 import com.android.systemui.battery.BatteryMeterViewController;
 import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.LightsOutNotifController;
+import com.android.systemui.statusbar.phone.LegacyLightsOutNotifController;
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
@@ -78,7 +79,9 @@
         getBatteryMeterViewController().init();
         getHeadsUpAppearanceController().init();
         getPhoneStatusBarViewController().init();
-        getLightsOutNotifController().init();
+        if (!NotificationsLiveDataStoreRefactor.isEnabled()) {
+            getLegacyLightsOutNotifController().init();
+        }
         getStatusBarDemoMode().init();
     }
 
@@ -101,7 +104,7 @@
 
     /** */
     @StatusBarFragmentScope
-    LightsOutNotifController getLightsOutNotifController();
+    LegacyLightsOutNotifController getLegacyLightsOutNotifController();
 
     /** */
     @StatusBarFragmentScope
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index b0532ce..0bdd1a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -36,7 +36,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.data.repository.StatusBarModeRepository
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
 import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -68,7 +68,7 @@
     private val dumpManager: DumpManager,
     private val statusBarWindowController: StatusBarWindowController,
     private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
-    private val statusBarModeRepository: StatusBarModeRepository,
+    private val statusBarModeRepository: StatusBarModeRepositoryStore,
 ) : CallbackController<OngoingCallListener>, Dumpable, CoreStartable {
     private var isFullscreen: Boolean = false
     /** Non-null if there's an active call notification. */
@@ -129,7 +129,7 @@
         dumpManager.registerDumpable(this)
         notifCollection.addCollectionListener(notifListener)
         scope.launch {
-            statusBarModeRepository.isInFullscreenMode.collect {
+            statusBarModeRepository.defaultDisplay.isInFullscreenMode.collect {
                 isFullscreen = it
                 updateChipClickListener()
                 updateGestureListening()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
index da9c45a..9c78ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
@@ -27,7 +27,7 @@
  *
  * This class is used to break a dependency cycle between
  * [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController] and
- * [com.android.systemui.statusbar.data.repository.StatusBarModeRepository]. Instead, those two
+ * [com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore]. Instead, those two
  * classes both refer to this repository.
  */
 @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index b9b88f4..7d7f49b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -16,11 +16,15 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.binder
 
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
 import android.view.View
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.launch
@@ -61,9 +65,49 @@
                         listener.onTransitionFromLockscreenToDreamStarted()
                     }
                 }
+
+                if (NotificationsLiveDataStoreRefactor.isEnabled) {
+                    val displayId = view.display.displayId
+                    val lightsOutView: View = view.requireViewById(R.id.notification_lights_out)
+                    launch {
+                        viewModel.areNotificationsLightsOut(displayId).collect { show ->
+                            animateLightsOutView(lightsOutView, show)
+                        }
+                    }
+                }
             }
         }
     }
+
+    private fun animateLightsOutView(view: View, visible: Boolean) {
+        view.animate().cancel()
+
+        val alpha = if (visible) 1f else 0f
+        val duration = if (visible) 750L else 250L
+        val visibility = if (visible) View.VISIBLE else View.GONE
+
+        if (visible) {
+            view.alpha = 0f
+            view.visibility = View.VISIBLE
+        }
+
+        view
+            .animate()
+            .alpha(alpha)
+            .setDuration(duration)
+            .setListener(
+                object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animation: Animator) {
+                        view.alpha = alpha
+                        view.visibility = visibility
+                        // Unset the listener, otherwise this may persist for
+                        // another view property animation
+                        view.animate().setListener(null)
+                    }
+                }
+            )
+            .start()
+    }
 }
 
 /** Listener for various events that may affect the status bar's visibility. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
index 15ab143..52a6d8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -20,11 +20,17 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
@@ -48,12 +54,25 @@
 
     /** Emits whenever a transition from lockscreen to dream has started. */
     val transitionFromLockscreenToDreamStartedEvent: Flow<Unit>
+
+    /**
+     * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where
+     * status bar and navigation icons dim. In this mode, a notification dot appears where the
+     * notification icons would appear if they would be shown outside of this mode.
+     *
+     * This flow tells when to show or hide the notification dot in the status bar to indicate
+     * whether there are notifications when the device is in
+     * [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE].
+     */
+    fun areNotificationsLightsOut(displayId: Int): Flow<Boolean>
 }
 
 @SysUISingleton
 class CollapsedStatusBarViewModelImpl
 @Inject
 constructor(
+    private val lightsOutInteractor: LightsOutInteractor,
+    private val notificationsInteractor: ActiveNotificationsInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     @Application coroutineScope: CoroutineScope,
 ) : CollapsedStatusBarViewModel {
@@ -69,4 +88,17 @@
         keyguardTransitionInteractor.lockscreenToDreamingTransition
             .filter { it.transitionState == TransitionState.STARTED }
             .map {}
+
+    override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> =
+        if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) {
+            emptyFlow()
+        } else {
+            combine(
+                    notificationsInteractor.areAnyNotificationsPresent,
+                    lightsOutInteractor.isLowProfile(displayId),
+                ) { hasNotifications, isLowProfile ->
+                    hasNotifications && isLowProfile
+                }
+                .distinctUntilChanged()
+        }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index 52133ee..ad2b070 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -130,7 +130,7 @@
     /**
      * If there are faces enrolled and user enabled face auth on keyguard.
      */
-    default boolean isFaceEnrolled() {
+    default boolean isFaceEnrolledAndEnabled() {
         return false;
     }
 
@@ -265,7 +265,7 @@
 
         /**
          * Triggered when face auth becomes available or unavailable. Value should be queried with
-         * {@link KeyguardStateController#isFaceEnrolled()}.
+         * {@link KeyguardStateController#isFaceEnrolledAndEnabled()}.
          */
         default void onFaceEnrolledChanged() {}
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 8cc7e7d2..3deb9e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -85,7 +85,7 @@
     private boolean mTrustManaged;
     private boolean mTrusted;
     private boolean mDebugUnlocked = false;
-    private boolean mFaceEnrolled;
+    private boolean mFaceEnrolledAndEnabled;
 
     private float mDismissAmount = 0f;
     private boolean mDismissingFromTouch = false;
@@ -260,16 +260,16 @@
                 || (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked);
         boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user);
         boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user);
-        boolean faceEnrolled = mKeyguardUpdateMonitor.isFaceEnrolled(user);
+        boolean faceEnabledAndEnrolled = mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled();
         boolean changed = secure != mSecure || canDismissLockScreen != mCanDismissLockScreen
                 || trustManaged != mTrustManaged || mTrusted != trusted
-                || mFaceEnrolled != faceEnrolled;
+                || mFaceEnrolledAndEnabled != faceEnabledAndEnrolled;
         if (changed || updateAlways) {
             mSecure = secure;
             mCanDismissLockScreen = canDismissLockScreen;
             mTrusted = trusted;
             mTrustManaged = trustManaged;
-            mFaceEnrolled = faceEnrolled;
+            mFaceEnrolledAndEnabled = faceEnabledAndEnrolled;
             mLogger.logKeyguardStateUpdate(
                     mSecure, mCanDismissLockScreen, mTrusted, mTrustManaged);
             notifyUnlockedChanged();
@@ -290,8 +290,8 @@
     }
 
     @Override
-    public boolean isFaceEnrolled() {
-        return mFaceEnrolled;
+    public boolean isFaceEnrolledAndEnabled() {
+        return mFaceEnrolledAndEnabled;
     }
 
     @Override
@@ -416,7 +416,7 @@
         pw.println("  mTrustManaged: " + mTrustManaged);
         pw.println("  mTrusted: " + mTrusted);
         pw.println("  mDebugUnlocked: " + mDebugUnlocked);
-        pw.println("  mFaceEnrolled: " + mFaceEnrolled);
+        pw.println("  mFaceEnrolled: " + mFaceEnrolledAndEnabled);
         pw.println("  isKeyguardFadingAway: " + isKeyguardFadingAway());
         pw.println("  isKeyguardGoingAway: " + isKeyguardGoingAway());
         pw.println("  isLaunchTransitionFadingAway: " + isLaunchTransitionFadingAway());
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 5fc435a..cf76c0d 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -29,7 +29,6 @@
 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.flags.FeatureFlags
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.user.data.model.SelectedUserModel
@@ -120,7 +119,6 @@
     @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val globalSettings: GlobalSettings,
     private val tracker: UserTracker,
-    featureFlags: FeatureFlags,
 ) : UserRepository {
 
     private val _userSwitcherSettings: StateFlow<UserSwitcherSettingsModel> =
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index d65a69c..9ee3d22 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -64,13 +64,13 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.settingslib.volume.MediaSessions;
 import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.util.RingerModeLiveData;
@@ -99,6 +99,7 @@
 @SysUISingleton
 public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
     private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class);
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int TOUCH_FEEDBACK_TIMEOUT_MS = 1000;
     private static final int DYNAMIC_STREAM_START_INDEX = 100;
@@ -1339,14 +1340,24 @@
 
         private boolean showForSession(Token token) {
             if (mVolumeAdjustmentForRemoteGroupSessions) {
+                if (DEBUG) {
+                    Log.d(TAG, "Volume adjustment for remote group sessions allowed,"
+                            + " showForSession: true");
+                }
                 return true;
             }
             MediaController ctr = new MediaController(mContext, token);
             String packageName = ctr.getPackageName();
             List<RoutingSessionInfo> sessions =
                     mRouter2Manager.getRoutingSessions(packageName);
-
+            if (DEBUG) {
+                Log.d(TAG, "Found " + sessions.size() + " routing sessions for package name "
+                        + packageName);
+            }
             for (RoutingSessionInfo session : sessions) {
+                if (DEBUG) {
+                    Log.d(TAG, "Found routingSessionInfo: " + session);
+                }
                 if (!session.isSystemSession()
                         && session.getVolumeHandling() != MediaRoute2Info.PLAYBACK_VOLUME_FIXED) {
                     return true;
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 2132904..5558aa7 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -214,14 +214,12 @@
                 Utils.getColorAttrDefaultColor(
                         this, com.android.internal.R.attr.colorAccentPrimary));
         mKeyguardFaceAuthInteractor.onWalletLaunched();
-        mKeyguardViewManager.requestFace(true);
     }
 
     @Override
     protected void onPause() {
         super.onPause();
         mKeyguardViewManager.requestFp(false, -1);
-        mKeyguardViewManager.requestFace(false);
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index e429446..3f76d30 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -261,7 +261,7 @@
 
         // GIVEN fingerprint and face are NOT enrolled
         activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
-        `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
+        `when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(false)
         `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(false)
 
         // WHEN unlock intent is allowed when NO biometrics are enrolled (0)
@@ -291,7 +291,7 @@
 
         // GIVEN fingerprint and face are both enrolled
         activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
-        `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true)
+        `when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true)
         `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(true)
 
         // WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs
@@ -314,7 +314,7 @@
         )
 
         // WHEN fingerprint ONLY enrolled
-        `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
+        `when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(false)
         `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(true)
 
         // THEN active unlock triggers allowed on unlock intent
@@ -325,7 +325,7 @@
         )
 
         // WHEN face ONLY enrolled
-        `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true)
+        `when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true)
         `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(false)
 
         // THEN active unlock triggers allowed on unlock intent
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 2e9b7e8..b403d1d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -125,10 +125,7 @@
                 repository = repository,
             )
 
-        withDeps.featureFlags.apply {
-            set(Flags.REGION_SAMPLING, false)
-            set(Flags.FACE_AUTH_REFACTOR, false)
-        }
+        withDeps.featureFlags.apply { set(Flags.REGION_SAMPLING, false) }
         underTest =
             ClockEventController(
                 withDeps.keyguardInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 7feab91..adf0ada 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -18,7 +18,6 @@
 
 import static android.view.View.INVISIBLE;
 
-import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
 import static com.android.systemui.flags.Flags.MIGRATE_CLOCKS_TO_BLUEPRINT;
 
@@ -179,7 +178,6 @@
         when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
         mExecutor = new FakeExecutor(new FakeSystemClock());
         mFakeFeatureFlags = new FakeFeatureFlags();
-        mFakeFeatureFlags.set(FACE_AUTH_REFACTOR, false);
         mFakeFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
         mFakeFeatureFlags.set(MIGRATE_CLOCKS_TO_BLUEPRINT, false);
         mController = new KeyguardClockSwitchController(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 225f125..543b291 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -47,6 +47,7 @@
 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
@@ -137,6 +138,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 faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var postureController: DevicePostureController
@@ -257,7 +259,7 @@
                 telephonyManager,
                 viewMediatorCallback,
                 audioManager,
-                mock(),
+                faceAuthInteractor,
                 mock(),
                 { JavaAdapter(sceneTestUtils.testScope.backgroundScope) },
                 mSelectedUserInteractor,
@@ -576,49 +578,12 @@
     }
 
     @Test
-    fun onSwipeUp_whenFaceDetectionIsNotRunning_initiatesFaceAuth() {
+    fun onSwipeUp_forwardsItToFaceAuthInteractor() {
         val registeredSwipeListener = registeredSwipeListener
-        whenever(keyguardUpdateMonitor.isFaceDetectionRunning).thenReturn(false)
         setupGetSecurityView(SecurityMode.Password)
         registeredSwipeListener.onSwipeUp()
-        verify(keyguardUpdateMonitor).requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER)
-    }
 
-    @Test
-    fun onSwipeUp_whenFaceDetectionIsRunning_doesNotInitiateFaceAuth() {
-        val registeredSwipeListener = registeredSwipeListener
-        whenever(keyguardUpdateMonitor.isFaceDetectionRunning).thenReturn(true)
-        registeredSwipeListener.onSwipeUp()
-        verify(keyguardUpdateMonitor, never())
-            .requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER)
-    }
-
-    @Test
-    fun onSwipeUp_whenFaceDetectionIsTriggered_hidesBouncerMessage() {
-        val registeredSwipeListener = registeredSwipeListener
-        whenever(
-                keyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER)
-            )
-            .thenReturn(true)
-        setupGetSecurityView(SecurityMode.Password)
-        clearInvocations(viewFlipperController)
-        registeredSwipeListener.onSwipeUp()
-        viewControllerImmediately
-        verify(keyguardPasswordViewControllerMock)
-            .showMessage(/* message= */ null, /* colorState= */ null, /* animated= */ true)
-    }
-
-    @Test
-    fun onSwipeUp_whenFaceDetectionIsNotTriggered_retainsBouncerMessage() {
-        val registeredSwipeListener = registeredSwipeListener
-        whenever(
-                keyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER)
-            )
-            .thenReturn(false)
-        setupGetSecurityView(SecurityMode.Password)
-        registeredSwipeListener.onSwipeUp()
-        verify(keyguardPasswordViewControllerMock, never())
-            .showMessage(/* message= */ null, /* colorState= */ null, /* animated= */ true)
+        verify(faceAuthInteractor).onSwipeUpOnBouncer()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index 146715d..13fb42c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -35,6 +35,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.power.data.repository.FakePowerRepository;
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -70,6 +71,7 @@
 
     @Mock protected KeyguardClockSwitch mKeyguardClockSwitch;
     @Mock protected FrameLayout mMediaHostContainer;
+    @Mock protected KeyguardStatusAreaView mKeyguardStatusAreaView;
 
     @Before
     public void setup() {
@@ -109,6 +111,8 @@
         when(mKeyguardStatusView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
         when(mKeyguardClockSwitchController.getView()).thenReturn(mKeyguardClockSwitch);
         when(mKeyguardTransitionInteractor.getGoneToAodTransition()).thenReturn(emptyFlow());
+        when(mKeyguardStatusView.findViewById(R.id.keyguard_status_area))
+                .thenReturn(mKeyguardStatusAreaView);
     }
 
     protected void givenViewAttached() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 948942f..9c3288b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.keyguard;
 
+import static junit.framework.Assert.assertEquals;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -27,6 +29,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.animation.AnimatorTestRule;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -40,6 +43,7 @@
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -51,6 +55,9 @@
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControllerBaseTest {
 
+    @Rule
+    public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
+
     @Test
     public void dozeTimeTick_updatesSlice() {
         mController.dozeTimeTick();
@@ -230,4 +237,34 @@
             throw new RuntimeException(e);
         }
     }
+
+    @Test
+    public void statusAreaHeightChange_animatesHeightOutputChange() {
+        // Init & Capture Layout Listener
+        mController.onInit();
+        mController.onViewAttached();
+
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+        ArgumentCaptor<View.OnLayoutChangeListener> captor =
+                ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+        verify(mKeyguardStatusAreaView).addOnLayoutChangeListener(captor.capture());
+        View.OnLayoutChangeListener listener = captor.getValue();
+
+        // Setup and validate initial height
+        when(mKeyguardStatusView.getHeight()).thenReturn(200);
+        when(mKeyguardClockSwitchController.getNotificationIconAreaHeight()).thenReturn(10);
+        assertEquals(190, mController.getLockscreenHeight());
+
+        // Trigger Change and validate value unchanged immediately
+        when(mKeyguardStatusAreaView.getHeight()).thenReturn(100);
+        when(mKeyguardStatusView.getHeight()).thenReturn(300);      // Include child height
+        listener.onLayoutChange(mKeyguardStatusAreaView,
+            /* new layout */ 100, 300, 200, 400,
+            /* old layout */ 100, 300, 200, 300);
+        assertEquals(190, mController.getLockscreenHeight());
+
+        // Complete animation, validate height increased
+        mAnimatorTestRule.advanceTimeBy(200);
+        assertEquals(290, mController.getLockscreenHeight());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 2b41e080..1ab634c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -23,24 +23,15 @@
 import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
 import static android.hardware.biometrics.SensorProperties.STRENGTH_CONVENIENCE;
 import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
-import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN;
-import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_STARTED_WAKING_UP;
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
 import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
 import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
-import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
-import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
 import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
 import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
-import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
 
@@ -88,12 +79,7 @@
 import android.hardware.biometrics.BiometricSourceType;
 import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.display.DisplayManagerGlobal;
-import android.hardware.face.FaceAuthenticateOptions;
 import android.hardware.face.FaceManager;
-import android.hardware.face.FaceSensorProperties;
-import android.hardware.face.FaceSensorPropertiesInternal;
-import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -121,11 +107,6 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.text.TextUtils;
-import android.view.Display;
-import android.view.DisplayAdjustments;
-import android.view.DisplayInfo;
-
-import androidx.annotation.NonNull;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.internal.jank.InteractionJankMonitor;
@@ -143,10 +124,13 @@
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
+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.FakeDisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -199,7 +183,6 @@
             TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
             DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
             TEST_CARRIER_ID, 0);
-    private static final int FACE_SENSOR_ID = 0;
     private static final int FINGERPRINT_SENSOR_ID = 1;
 
     @Mock
@@ -217,8 +200,6 @@
     @Mock
     private FingerprintManager mFingerprintManager;
     @Mock
-    private FaceManager mFaceManager;
-    @Mock
     private BiometricManager mBiometricManager;
     @Mock
     private PackageManager mPackageManager;
@@ -277,19 +258,18 @@
     @Mock
     private IActivityTaskManager mActivityTaskManager;
     @Mock
-    private WakefulnessLifecycle mWakefulness;
-    @Mock
     private SelectedUserInteractor mSelectedUserInteractor;
+    @Mock
+    private KeyguardFaceAuthInteractor mFaceAuthInteractor;
+    @Captor
+    private ArgumentCaptor<FaceAuthenticationListener> mFaceAuthenticationListener;
 
-    private List<FaceSensorPropertiesInternal> mFaceSensorProperties;
     private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
     private final int mCurrentUserId = 100;
 
     @Captor
     private ArgumentCaptor<IBiometricEnabledOnKeyguardCallback>
             mBiometricEnabledCallbackArgCaptor;
-    @Captor
-    private ArgumentCaptor<FaceManager.AuthenticationCallback> mAuthenticationCallbackCaptor;
 
     // Direct executor
     private final Executor mBackgroundExecutor = Runnable::run;
@@ -303,14 +283,11 @@
     private FaceWakeUpTriggersConfig mFaceWakeUpTriggersConfig;
     private IFingerprintAuthenticatorsRegisteredCallback
             mFingerprintAuthenticatorsRegisteredCallback;
-    private IFaceAuthenticatorsRegisteredCallback mFaceAuthenticatorsRegisteredCallback;
     private final InstanceId mKeyguardInstanceId = InstanceId.fakeInstanceId(999);
-    private FakeDisplayTracker mDisplayTracker;
 
     @Before
     public void setup() throws RemoteException {
         MockitoAnnotations.initMocks(this);
-        mDisplayTracker = new FakeDisplayTracker(mContext);
         when(mSessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(mKeyguardInstanceId);
 
         when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
@@ -358,12 +335,14 @@
 
         mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
         setupBiometrics(mKeyguardUpdateMonitor);
+        mKeyguardUpdateMonitor.setFaceAuthInteractor(mFaceAuthInteractor);
+        verify(mFaceAuthInteractor).registerListener(mFaceAuthenticationListener.capture());
     }
 
     private void setupBiometrics(KeyguardUpdateMonitor keyguardUpdateMonitor)
             throws RemoteException {
         captureAuthenticatorsRegisteredCallbacks();
-        setupFaceAuth(/* isClass3 */ false);
+        when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(false);
         setupFingerprintAuth(/* isClass3 */ true);
 
         verify(mBiometricManager)
@@ -387,12 +366,6 @@
     }
 
     private void captureAuthenticatorsRegisteredCallbacks() throws RemoteException {
-        ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> faceCaptor =
-                ArgumentCaptor.forClass(IFaceAuthenticatorsRegisteredCallback.class);
-        verify(mFaceManager).addAuthenticatorsRegisteredCallback(faceCaptor.capture());
-        mFaceAuthenticatorsRegisteredCallback = faceCaptor.getValue();
-        mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
-
         ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> fingerprintCaptor =
                 ArgumentCaptor.forClass(IFingerprintAuthenticatorsRegisteredCallback.class);
         verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -402,16 +375,6 @@
                 .onAllAuthenticatorsRegistered(mFingerprintSensorProperties);
     }
 
-    private void setupFaceAuth(boolean isClass3) throws RemoteException {
-        when(mFaceManager.isHardwareDetected()).thenReturn(true);
-        when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true);
-        mFaceSensorProperties =
-                List.of(createFaceSensorProperties(/* supportsFaceDetection = */ false, isClass3));
-        when(mFaceManager.getSensorPropertiesInternal()).thenReturn(mFaceSensorProperties);
-        mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
-        assertEquals(isClass3, mKeyguardUpdateMonitor.isFaceClass3());
-    }
-
     private void setupFingerprintAuth(boolean isClass3) throws RemoteException {
         when(mAuthController.isFingerprintEnrolled(anyInt())).thenReturn(true);
         when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
@@ -442,28 +405,6 @@
                 true /* resetLockoutRequiresHardwareAuthToken */);
     }
 
-    @NonNull
-    private FaceSensorPropertiesInternal createFaceSensorProperties(
-            boolean supportsFaceDetection, boolean isClass3) {
-        final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
-        componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
-                "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
-                "00000001" /* serialNumber */, "" /* softwareVersion */));
-        componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
-                "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
-                "vendor/version/revision" /* softwareVersion */));
-
-        return new FaceSensorPropertiesInternal(
-                FACE_SENSOR_ID /* id */,
-                isClass3 ? STRENGTH_STRONG : STRENGTH_CONVENIENCE,
-                1 /* maxTemplatesAllowed */,
-                componentInfo,
-                FaceSensorProperties.TYPE_UNKNOWN,
-                supportsFaceDetection /* supportsFaceDetection */,
-                true /* supportsSelfIllumination */,
-                false /* resetLockoutRequiresChallenge */);
-    }
-
     @After
     public void tearDown() {
         if (mMockitoSession != null) {
@@ -887,13 +828,9 @@
     }
 
     @Test
-    public void whenDetectFace_biometricDetectCallback() throws RemoteException {
-        ArgumentCaptor<FaceManager.FaceDetectionCallback> faceDetectCallbackCaptor =
-                ArgumentCaptor.forClass(FaceManager.FaceDetectionCallback.class);
-
-        givenDetectFace();
-        verify(mFaceManager).detectFace(any(), faceDetectCallbackCaptor.capture(), any());
-        faceDetectCallbackCaptor.getValue().onFaceDetected(0, 0, false);
+    public void whenDetectFace_biometricDetectCallback() {
+        mFaceAuthenticationListener.getValue().onDetectionStatusChanged(
+                new FaceDetectionStatus(0, 0, false, 0L));
 
         // THEN verify keyguardUpdateMonitorCallback receives a detect callback
         // and NO authenticate callbacks
@@ -926,40 +863,10 @@
     }
 
     @Test
-    public void class3FingerprintLockOut_lockOutClass1Face() throws RemoteException {
-        setupFaceAuth(/* isClass3 */ false);
-        setupFingerprintAuth(/* isClass3 */ true);
-
-        // GIVEN primary auth is not required by StrongAuthTracker
-        primaryAuthNotRequiredByStrongAuthTracker();
-
-        // WHEN fingerprint (class 3) is lock out
-        fingerprintErrorTemporaryLockOut();
-
-        // THEN unlocking with face is not allowed
-        Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                BiometricSourceType.FACE));
-    }
-
-    @Test
-    public void class3FingerprintLockOut_lockOutClass3Face() throws RemoteException {
-        setupFaceAuth(/* isClass3 */ true);
-        setupFingerprintAuth(/* isClass3 */ true);
-
-        // GIVEN primary auth is not required by StrongAuthTracker
-        primaryAuthNotRequiredByStrongAuthTracker();
-
-        // WHEN fingerprint (class 3) is lock out
-        fingerprintErrorTemporaryLockOut();
-
-        // THEN unlocking with face is not allowed
-        Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                BiometricSourceType.FACE));
-    }
-
-    @Test
     public void class3FaceLockOut_lockOutClass3Fingerprint() throws RemoteException {
-        setupFaceAuth(/* isClass3 */ true);
+        when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(true);
+        when(mFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()).thenReturn(true);
+
         setupFingerprintAuth(/* isClass3 */ true);
 
         // GIVEN primary auth is not required by StrongAuthTracker
@@ -975,7 +882,7 @@
 
     @Test
     public void class1FaceLockOut_doesNotLockOutClass3Fingerprint() throws RemoteException {
-        setupFaceAuth(/* isClass3 */ false);
+        when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(false);
         setupFingerprintAuth(/* isClass3 */ true);
 
         // GIVEN primary auth is not required by StrongAuthTracker
@@ -1056,162 +963,6 @@
     }
 
     @Test
-    public void testTriesToAuthenticate_whenBouncer() {
-        setKeyguardBouncerVisibility(true);
-        verifyFaceAuthenticateCall();
-    }
-
-    @Test
-    public void testNoStartAuthenticate_whenAboutToShowBouncer() {
-        mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(
-                /* bouncerIsOrWillBeShowing */ true, /* bouncerFullyShown */ false);
-
-        verifyFaceAuthenticateNeverCalled();
-    }
-
-    @Test
-    public void testTriesToAuthenticate_whenKeyguard() {
-        keyguardIsVisible();
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-
-        verifyFaceAuthenticateCall();
-        verify(mUiEventLogger).logWithInstanceIdAndPosition(
-                eq(FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP),
-                eq(0),
-                eq(null),
-                any(),
-                eq(PowerManager.WAKE_REASON_POWER_BUTTON));
-    }
-
-    @Test
-    public void skipsAuthentication_whenStatusBarShadeLocked() {
-        mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-
-        keyguardIsVisible();
-        verifyFaceAuthenticateNeverCalled();
-    }
-
-    @Test
-    public void skipsAuthentication_whenStrongAuthRequired_nonBypass() {
-        lockscreenBypassIsNotAllowed();
-        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
-
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-        keyguardIsVisible();
-
-        verifyFaceAuthenticateNeverCalled();
-    }
-
-    @Test
-    public void nofaceDetect_whenStrongAuthRequiredAndBypassUdfpsSupportedAndFpRunning()
-            throws RemoteException {
-        // GIVEN bypass is enabled, face detection is supported
-        lockscreenBypassIsAllowed();
-        supportsFaceDetection();
-        keyguardIsVisible();
-
-        // GIVEN udfps is supported and strong auth required for weak biometrics (face) only
-        givenUdfpsSupported();
-        primaryAuthRequiredForWeakBiometricOnly(); // allows class3 fp to run but not class1 face
-
-        // WHEN the device wakes up
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-
-        // THEN face detect and authenticate are NOT triggered
-        verifyFaceDetectNeverCalled();
-        verifyFaceAuthenticateNeverCalled();
-
-        // THEN biometric help message sent to callback
-        verify(mTestCallback).onBiometricHelp(
-                eq(BIOMETRIC_HELP_FACE_NOT_AVAILABLE), anyString(), eq(BiometricSourceType.FACE));
-    }
-
-    @Test
-    public void faceDetect_whenStrongAuthRequiredAndBypass() throws RemoteException {
-        givenDetectFace();
-
-        // FACE detect is triggered, not authenticate
-        verifyFaceDetectCall();
-        verifyFaceAuthenticateNeverCalled();
-
-        // WHEN bouncer becomes visible
-        setKeyguardBouncerVisibility(true);
-        clearInvocations(mFaceManager);
-
-        // THEN face scanning is not run
-        mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
-        verifyFaceAuthenticateNeverCalled();
-        verifyFaceDetectNeverCalled();
-    }
-
-    @Test
-    public void noFaceDetect_whenStrongAuthRequiredAndBypass_faceDetectionUnsupported() {
-        // GIVEN bypass is enabled, face detection is NOT supported and strong auth is required
-        lockscreenBypassIsAllowed();
-        primaryAuthRequiredEncrypted();
-        keyguardIsVisible();
-
-        // WHEN the device wakes up
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-
-        // FACE detect and authenticate are NOT triggered
-        verifyFaceDetectNeverCalled();
-        verifyFaceAuthenticateNeverCalled();
-    }
-
-    @Test
-    public void requestFaceAuth_whenFaceAuthWasStarted_returnsTrue() throws RemoteException {
-        // This satisfies all the preconditions to run face auth.
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        bouncerFullyVisibleAndNotGoingToSleep();
-        mTestableLooper.processAllMessages();
-
-        boolean didFaceAuthRun = mKeyguardUpdateMonitor.requestFaceAuth(
-                NOTIFICATION_PANEL_CLICKED);
-
-        assertThat(didFaceAuthRun).isTrue();
-    }
-
-    @Test
-    public void requestFaceAuth_whenFaceAuthWasNotStarted_returnsFalse() throws RemoteException {
-        // This ensures face auth won't run.
-        biometricsDisabledForCurrentUser();
-        mTestableLooper.processAllMessages();
-
-        boolean didFaceAuthRun = mKeyguardUpdateMonitor.requestFaceAuth(
-                NOTIFICATION_PANEL_CLICKED);
-
-        assertThat(didFaceAuthRun).isFalse();
-    }
-
-    @Test
-    public void testTriesToAuthenticate_whenAssistant() {
-        mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
-        mKeyguardUpdateMonitor.setAssistantVisible(true);
-
-        verifyFaceAuthenticateCall();
-    }
-
-    @Test
-    public void doesNotTryToAuthenticateWhenKeyguardIsNotShowingButOccluded_whenAssistant() {
-        mKeyguardUpdateMonitor.setKeyguardShowing(false, true);
-        mKeyguardUpdateMonitor.setAssistantVisible(true);
-
-        verifyFaceAuthenticateNeverCalled();
-    }
-
-    @Test
     public void noFpListeningWhenKeyguardIsOccluded_unlessAlternateBouncerShowing() {
         // GIVEN device is awake but occluded
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
@@ -1257,62 +1008,6 @@
     }
 
     @Test
-    public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() {
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-        lockscreenBypassIsAllowed();
-        mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
-                mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */,
-                new ArrayList<>());
-        keyguardIsVisible();
-        verifyFaceAuthenticateCall();
-    }
-
-    @Test
-    public void faceUnlockDoesNotRunWhenDeviceIsGoingToSleepWithAssistantVisible() {
-        mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
-        mKeyguardUpdateMonitor.setAssistantVisible(true);
-
-        verifyFaceAuthenticateCall();
-        mTestableLooper.processAllMessages();
-        clearInvocations(mFaceManager);
-
-        // Device going to sleep while assistant is visible
-        mKeyguardUpdateMonitor.handleStartedGoingToSleep(0);
-        mKeyguardUpdateMonitor.handleFinishedGoingToSleep(0);
-        mTestableLooper.moveTimeForward(DEFAULT_CANCEL_SIGNAL_TIMEOUT);
-        mTestableLooper.processAllMessages();
-
-        mKeyguardUpdateMonitor.handleKeyguardReset();
-
-        assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isFalse();
-        verifyFaceAuthenticateNeverCalled();
-    }
-
-    @Test
-    public void testIgnoresAuth_whenTrustAgentOnKeyguard_withoutBypass() {
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-        mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
-                mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */, new ArrayList<>());
-        keyguardIsVisible();
-        verifyFaceAuthenticateNeverCalled();
-    }
-
-    @Test
-    public void testNoFaceAuth_whenLockDown() {
-        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
-        userDeviceLockDown();
-
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        keyguardIsVisible();
-        mTestableLooper.processAllMessages();
-
-        verifyFaceAuthenticateNeverCalled();
-        verifyFaceDetectNeverCalled();
-    }
-
-    @Test
     public void testFingerprintPowerPressed_restartsFingerprintListeningStateWithDelay() {
         mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
                 .onAuthenticationError(FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, "");
@@ -1329,18 +1024,6 @@
     }
 
     @Test
-    public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() {
-        // test whether face will be skipped if authenticated, so the value of isClass3Biometric
-        // doesn't matter here
-        mKeyguardUpdateMonitor.onFaceAuthenticated(mSelectedUserInteractor.getSelectedUserId(),
-                true /* isClass3Biometric */);
-        setKeyguardBouncerVisibility(true);
-        mTestableLooper.processAllMessages();
-
-        verifyFaceAuthenticateNeverCalled();
-    }
-
-    @Test
     public void testFaceAndFingerprintLockout_onlyFace() {
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
@@ -1379,7 +1062,11 @@
     @Test
     public void testGetUserCanSkipBouncer_whenFace() {
         int user = mSelectedUserInteractor.getSelectedUserId();
-        mKeyguardUpdateMonitor.onFaceAuthenticated(user, true /* isClass3Biometric */);
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(true /* isClass3Biometric */))
+                .thenReturn(true);
+        when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(true);
+        when(mFaceAuthInteractor.isAuthenticated()).thenReturn(true);
+
         assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
     }
 
@@ -1388,7 +1075,9 @@
         when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isClass3Biometric */))
                 .thenReturn(false);
         int user = mSelectedUserInteractor.getSelectedUserId();
-        mKeyguardUpdateMonitor.onFaceAuthenticated(user, false /* isClass3Biometric */);
+        when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(false);
+        when(mFaceAuthInteractor.isAuthenticated()).thenReturn(true);
+
         assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse();
     }
 
@@ -1409,22 +1098,19 @@
     }
 
     @Test
-    public void testBiometricsCleared_whenUserSwitches() throws Exception {
+    public void testBiometricsCleared_whenUserSwitches() {
         final BiometricAuthenticated dummyAuthentication =
                 new BiometricAuthenticated(true /* authenticated */, true /* strong */);
-        mKeyguardUpdateMonitor.mUserFaceAuthenticated.put(0 /* user */, dummyAuthentication);
         mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.put(0 /* user */, dummyAuthentication);
         assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1);
-        assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1);
 
         mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, () -> {
         });
         assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0);
-        assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0);
     }
 
     @Test
-    public void testMultiUserJankMonitor_whenUserSwitches() throws Exception {
+    public void testMultiUserJankMonitor_whenUserSwitches() {
         mKeyguardUpdateMonitor.handleUserSwitchComplete(10 /* user */);
         verify(mInteractionJankMonitor).end(InteractionJankMonitor.CUJ_USER_SWITCH);
         verify(mLatencyTracker).onActionEnd(LatencyTracker.ACTION_USER_SWITCH);
@@ -1432,22 +1118,17 @@
 
     @Test
     public void testMultiUserLockoutChanged_whenUserSwitches() {
-        testMultiUserLockout_whenUserSwitches(BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT,
-                BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT);
+        testMultiUserLockout_whenUserSwitches(BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT);
     }
 
     @Test
     public void testMultiUserLockoutNotChanged_whenUserSwitches() {
-        testMultiUserLockout_whenUserSwitches(BiometricConstants.BIOMETRIC_LOCKOUT_NONE,
-                BiometricConstants.BIOMETRIC_LOCKOUT_NONE);
+        testMultiUserLockout_whenUserSwitches(BiometricConstants.BIOMETRIC_LOCKOUT_NONE);
     }
 
     private void testMultiUserLockout_whenUserSwitches(
-            @BiometricConstants.LockoutMode int fingerprintLockoutMode,
-            @BiometricConstants.LockoutMode int faceLockoutMode) {
+            @BiometricConstants.LockoutMode int fingerprintLockoutMode) {
         final int newUser = 12;
-        final boolean faceLockOut =
-                faceLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
         final boolean fpLockOut =
                 fingerprintLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
 
@@ -1455,16 +1136,11 @@
         mTestableLooper.processAllMessages();
         keyguardIsVisible();
 
-        verifyFaceAuthenticateCall();
         verifyFingerprintAuthenticateCall();
 
         when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser)))
                 .thenReturn(fingerprintLockoutMode);
-        when(mFaceManager.getLockoutModeForUser(eq(FACE_SENSOR_ID), eq(newUser)))
-                .thenReturn(faceLockoutMode);
-        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
         final CancellationSignal fpCancel = spy(mKeyguardUpdateMonitor.mFingerprintCancelSignal);
-        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
         mKeyguardUpdateMonitor.mFingerprintCancelSignal = fpCancel;
         KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
         mKeyguardUpdateMonitor.registerCallback(callback);
@@ -1472,17 +1148,13 @@
         mKeyguardUpdateMonitor.handleUserSwitchComplete(newUser);
         mTestableLooper.processAllMessages();
 
-        // THEN face and fingerprint listening are always cancelled immediately
-        verify(faceCancel).cancel();
-        verify(callback).onBiometricRunningStateChanged(
-                eq(false), eq(BiometricSourceType.FACE));
+        // THEN fingerprint listening are always cancelled immediately
         verify(fpCancel).cancel();
         verify(callback).onBiometricRunningStateChanged(
                 eq(false), eq(BiometricSourceType.FINGERPRINT));
 
         // THEN locked out states are updated
         assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(fpLockOut);
-        assertThat(mKeyguardUpdateMonitor.isFaceLockedOut()).isEqualTo(faceLockOut);
 
         // Fingerprint should be cancelled on lockout if going to lockout state, else
         // restarted if it's not
@@ -1752,11 +1424,11 @@
     public void testShouldNotListenForUdfps_whenFaceAuthenticated() {
         // GIVEN a "we should listen for udfps" state
         mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
+        when(mFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()).thenReturn(true);
         when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
 
         // WHEN face authenticated
-        mKeyguardUpdateMonitor.onFaceAuthenticated(
-                mSelectedUserInteractor.getSelectedUserId(), false);
+        when(mFaceAuthInteractor.isAuthenticated()).thenReturn(true);
 
         // THEN we shouldn't listen for udfps
         assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
@@ -1777,51 +1449,6 @@
     }
 
     @Test
-    public void testShouldNotUpdateBiometricListeningStateOnStatusBarStateChange() {
-        // GIVEN state for face auth should run aside from StatusBarState
-        biometricsNotDisabledThroughDevicePolicyManager();
-        mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
-        setKeyguardBouncerVisibility(false /* isVisible */);
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        lockscreenBypassIsAllowed();
-        keyguardIsVisible();
-
-        // WHEN status bar state reports a change to the keyguard that would normally indicate to
-        // start running face auth
-        mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isEqualTo(true);
-
-        // THEN face unlock is not running b/c status bar state changes don't cause biometric
-        // listening state to update
-        assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isEqualTo(false);
-
-        // WHEN biometric listening state is updated when showing state changes from false => true
-        mKeyguardUpdateMonitor.setKeyguardShowing(false, false);
-        mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
-
-        // THEN face unlock is running
-        assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isEqualTo(true);
-    }
-
-    @Test
-    public void testRequestFaceAuthFromOccludingApp_whenInvoked_startsFaceAuth() {
-        mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
-
-        assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isTrue();
-    }
-
-    @Test
-    public void testRequestFaceAuthFromOccludingApp_whenInvoked_stopsFaceAuth() {
-        mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
-
-        assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isTrue();
-
-        mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
-
-        assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isFalse();
-    }
-
-    @Test
     public void testRequireUnlockForNfc_Broadcast() {
         KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
         mKeyguardUpdateMonitor.registerCallback(callback);
@@ -1833,13 +1460,6 @@
     }
 
     @Test
-    public void testFaceDoesNotAuth_afterPinAttempt() {
-        mTestableLooper.processAllMessages();
-        mKeyguardUpdateMonitor.setCredentialAttempted();
-        verifyFaceAuthenticateNeverCalled();
-    }
-
-    @Test
     public void testShowTrustGrantedMessage_onTrustGranted() {
         // WHEN trust is enabled (ie: via some trust agent) with a trustGranted string
         mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
@@ -1855,366 +1475,17 @@
     }
 
     @Test
-    public void testShouldListenForFace_whenFaceManagerNotAvailable_returnsFalse() {
-        cleanupKeyguardUpdateMonitor();
-        mFaceManager = null;
-
-        mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenFpIsLockedOut_returnsFalse() throws RemoteException {
-        // Face auth should run when the following is true.
-        keyguardNotGoingAway();
-        occludingAppRequestsFaceAuth();
-        currentUserIsSystem();
-        primaryAuthNotRequiredByStrongAuthTracker();
-        biometricsEnabledForCurrentUser();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        userNotCurrentlySwitching();
-        mTestableLooper.processAllMessages();
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-
-        // Fingerprint is locked out.
-        fingerprintErrorTemporaryLockOut();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenFaceIsAlreadyAuthenticated_returnsFalse()
-            throws RemoteException {
-        // Face auth should run when the following is true.
-        bouncerFullyVisibleAndNotGoingToSleep();
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        primaryAuthNotRequiredByStrongAuthTracker();
-        biometricsEnabledForCurrentUser();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        userNotCurrentlySwitching();
-
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-
-        triggerSuccessfulFaceAuth();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenFpIsAlreadyAuthenticated_returnsFalse()
-            throws RemoteException {
-        // Face auth should run when the following is true.
-        bouncerFullyVisibleAndNotGoingToSleep();
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        primaryAuthNotRequiredByStrongAuthTracker();
-        biometricsEnabledForCurrentUser();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        userNotCurrentlySwitching();
-
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-
-        successfulFingerprintAuth();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
-        cleanupKeyguardUpdateMonitor();
-        // This disables face auth
-        when(mUserManager.isSystemUser()).thenReturn(false);
-        mKeyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(mContext);
-
-        // Face auth should run when the following is true.
-        keyguardNotGoingAway();
-        bouncerFullyVisibleAndNotGoingToSleep();
-        primaryAuthNotRequiredByStrongAuthTracker();
-        biometricsEnabledForCurrentUser();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        userNotCurrentlySwitching();
-        mTestableLooper.processAllMessages();
-
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenStrongAuthDoesNotAllowScanning_returnsFalse()
-            throws RemoteException {
-        // Face auth should run when the following is true.
-        keyguardNotGoingAway();
-        bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsSystem();
-        biometricsEnabledForCurrentUser();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        userNotCurrentlySwitching();
-
-        // This disables face auth
-        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenBiometricsDisabledForUser_returnsFalse()
-            throws RemoteException {
-        keyguardNotGoingAway();
-        bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-
-        // This disables face auth
-        biometricsDisabledForCurrentUser();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenUserCurrentlySwitching_returnsFalse()
-            throws RemoteException {
-        // Face auth should run when the following is true.
-        keyguardNotGoingAway();
-        bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-
-        userCurrentlySwitching();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenSecureCameraLaunched_returnsFalse()
-            throws RemoteException {
-        keyguardNotGoingAway();
-        bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-
-        secureCameraLaunched();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-    }
-
-    @Test
-    public void shouldListenForFace_secureCameraLaunchedButAlternateBouncerIsLaunched_returnsTrue()
-            throws RemoteException {
-        // Face auth should run when the following is true.
-        keyguardNotGoingAway();
-        bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        mTestableLooper.processAllMessages();
-
-        secureCameraLaunched();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-
-        alternateBouncerVisible();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenBouncerShowingAndDeviceIsAwake_returnsTrue()
-            throws RemoteException {
-        // Face auth should run when the following is true.
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-
-        bouncerFullyVisibleAndNotGoingToSleep();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenAuthInterruptIsActive_returnsTrue()
-            throws RemoteException {
-        // Face auth should run when the following is true.
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-
-        triggerAuthInterrupt();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenKeyguardIsAwake_returnsTrue() throws RemoteException {
-        // Preconditions for face auth to run
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-
-        statusBarShadeIsLocked();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-
-        deviceNotGoingToSleep();
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-        deviceIsInteractive();
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-        keyguardIsVisible();
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-        statusBarShadeIsNotLocked();
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenUdfpsFingerDown_returnsTrue() throws RemoteException {
-        // Preconditions for face auth to run
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-
-        when(mAuthController.isUdfpsFingerDown()).thenReturn(true);
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-    }
-
-    @Test
-    public void testShouldListenForFace_whenAlternateBouncerIsShowing_returnsTrue()
-            throws RemoteException {
-        // Preconditions for face auth to run
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        mTestableLooper.processAllMessages();
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-
-        mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-    }
-
-    @Test
-    public void testShouldListenForFace_alternateBouncerShowingButDeviceGoingToSleep_returnsFalse()
-            throws RemoteException {
-        // Preconditions for face auth to run
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        deviceNotGoingToSleep();
-        alternateBouncerVisible();
-        mTestableLooper.processAllMessages();
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-
-        deviceGoingToSleep();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-    }
-
-    private void alternateBouncerVisible() {
-        mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
-    }
-
-    @Test
-    public void testShouldListenForFace_whenFaceIsLockedOut_returnsTrue()
-            throws RemoteException {
-        // Preconditions for face auth to run
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
-        mTestableLooper.processAllMessages();
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-
-        // Face is locked out.
-        faceAuthLockOut();
-        mTestableLooper.processAllMessages();
-
-        // This is needed beccause we want to show face locked out error message whenever face auth
-        // is supposed to run.
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-    }
-
-    @Test
     public void testFingerprintCanAuth_whenCancellationNotReceivedAndAuthFailed() {
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
         keyguardIsVisible();
 
-        verifyFaceAuthenticateCall();
         verifyFingerprintAuthenticateCall();
 
-        mKeyguardUpdateMonitor.onFaceAuthenticated(0, false);
+        when(mFaceAuthInteractor.isAuthenticated()).thenReturn(true);
+        when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(false);
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isClass3Biometric */))
+                .thenReturn(false);
         // Make sure keyguard is going away after face auth attempt, and that it calls
         // updateBiometricStateListeningState.
         mKeyguardUpdateMonitor.setKeyguardShowing(false, false);
@@ -2234,34 +1505,6 @@
     }
 
     @Test
-    public void testDreamingStopped_faceDoesNotRun() {
-        mKeyguardUpdateMonitor.dispatchDreamingStopped();
-        mTestableLooper.processAllMessages();
-
-        verifyFaceAuthenticateNeverCalled();
-    }
-
-    @Test
-    public void testFaceWakeupTrigger_runFaceAuth_onlyOnConfiguredTriggers() {
-        // keyguard is visible
-        keyguardIsVisible();
-
-        // WHEN device wakes up from an application
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_APPLICATION);
-        mTestableLooper.processAllMessages();
-
-        // THEN face auth isn't triggered
-        verifyFaceAuthenticateNeverCalled();
-
-        // WHEN device wakes up from the power button
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-
-        // THEN face auth is triggered
-        verifyFaceAuthenticateCall();
-    }
-
-    @Test
     public void testOnTrustGrantedForCurrentUser_dismissKeyguardRequested_deviceInteractive() {
         // GIVEN device is interactive
         deviceIsInteractive();
@@ -2422,18 +1665,15 @@
     }
 
     @Test
-    public void testStrongAuthChange_lockDown_stopsFpAndFaceListeningState() {
-        // GIVEN device is listening for face and fingerprint
+    public void testStrongAuthChange_lockDown_stopsFpListeningState() {
+        // GIVEN device is listening for fingerprint
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
         keyguardIsVisible();
 
-        verifyFaceAuthenticateCall();
         verifyFingerprintAuthenticateCall();
 
-        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
         final CancellationSignal fpCancel = spy(mKeyguardUpdateMonitor.mFingerprintCancelSignal);
-        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
         mKeyguardUpdateMonitor.mFingerprintCancelSignal = fpCancel;
         KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
         mKeyguardUpdateMonitor.registerCallback(callback);
@@ -2445,88 +1685,13 @@
                 mSelectedUserInteractor.getSelectedUserId());
         mTestableLooper.processAllMessages();
 
-        // THEN face and fingerprint listening are cancelled
-        verify(faceCancel).cancel();
-        verify(callback).onBiometricRunningStateChanged(
-                eq(false), eq(BiometricSourceType.FACE));
+        // THEN fingerprint listening are cancelled
         verify(fpCancel).cancel();
         verify(callback).onBiometricRunningStateChanged(
                 eq(false), eq(BiometricSourceType.FINGERPRINT));
     }
 
     @Test
-    public void testNonStrongBiometricAllowedChanged_stopsFaceListeningState() {
-        // GIVEN device is listening for face and fingerprint
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-        keyguardIsVisible();
-
-        verifyFaceAuthenticateCall();
-
-        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
-        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
-        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
-        mKeyguardUpdateMonitor.registerCallback(callback);
-
-        // WHEN non-strong biometric allowed changes
-        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
-        mKeyguardUpdateMonitor.notifyNonStrongBiometricAllowedChanged(
-                mSelectedUserInteractor.getSelectedUserId());
-        mTestableLooper.processAllMessages();
-
-        // THEN face and fingerprint listening are cancelled
-        verify(faceCancel).cancel();
-        verify(callback).onBiometricRunningStateChanged(
-                eq(false), eq(BiometricSourceType.FACE));
-    }
-
-    @Test
-    public void testPostureChangeToUnsupported_stopsFaceListeningState() {
-        // GIVEN device is listening for face
-        mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_CLOSED;
-        deviceInPostureStateClosed();
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-        keyguardIsVisible();
-
-        verifyFaceAuthenticateCall();
-
-        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
-        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
-        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
-        mKeyguardUpdateMonitor.registerCallback(callback);
-
-        // WHEN device is opened
-        deviceInPostureStateOpened();
-        mTestableLooper.processAllMessages();
-
-        // THEN face listening is stopped.
-        verify(faceCancel).cancel();
-        verify(callback).onBiometricRunningStateChanged(
-                eq(false), eq(BiometricSourceType.FACE));
-    }
-
-    @Test
-    public void testShouldListenForFace_withLockedDown_returnsFalse()
-            throws RemoteException {
-        keyguardNotGoingAway();
-        bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        supportsFaceDetection();
-        mTestableLooper.processAllMessages();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-
-        userDeviceLockDown();
-
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-    }
-
-    @Test
     public void assistantVisible_requestActiveUnlock() {
         // GIVEN active unlock requests from the assistant are allowed
         when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2549,8 +1714,7 @@
     }
 
     @Test
-    public void fingerprintFailure_requestActiveUnlock_dismissKeyguard()
-            throws RemoteException {
+    public void fingerprintFailure_requestActiveUnlock_dismissKeyguard() {
         // GIVEN shouldTriggerActiveUnlock
         bouncerFullyVisible();
         when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
@@ -2571,8 +1735,7 @@
     }
 
     @Test
-    public void faceNonBypassFailure_requestActiveUnlock_doesNotDismissKeyguard()
-            throws RemoteException {
+    public void faceNonBypassFailure_requestActiveUnlock_doesNotDismissKeyguard() {
         // GIVEN shouldTriggerActiveUnlock
         when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
         keyguardIsVisible();
@@ -2588,7 +1751,8 @@
 
         // WHEN face fails & bypass is not allowed
         lockscreenBypassIsNotAllowed();
-        mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
+        mFaceAuthenticationListener.getValue().onAuthenticationStatusChanged(
+                new FailedFaceAuthenticationStatus());
 
         // THEN request unlock with NO keyguard dismissal
         verify(mTrustManager).reportUserRequestedUnlock(
@@ -2597,10 +1761,10 @@
     }
 
     @Test
-    public void faceBypassFailure_requestActiveUnlock_dismissKeyguard()
-            throws RemoteException {
+    public void faceBypassFailure_requestActiveUnlock_dismissKeyguard() {
         // GIVEN shouldTriggerActiveUnlock
         when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
+        when(mFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()).thenReturn(true);
         keyguardIsVisible();
         keyguardNotGoingAway();
         statusBarShadeIsNotLocked();
@@ -2614,7 +1778,8 @@
 
         // WHEN face fails & bypass is not allowed
         lockscreenBypassIsAllowed();
-        mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
+        mFaceAuthenticationListener.getValue().onAuthenticationStatusChanged(
+                new FailedFaceAuthenticationStatus());
 
         // THEN request unlock with a keyguard dismissal
         verify(mTrustManager).reportUserRequestedUnlock(
@@ -2623,10 +1788,10 @@
     }
 
     @Test
-    public void faceNonBypassFailure_requestActiveUnlock_dismissKeyguard()
-            throws RemoteException {
+    public void faceNonBypassFailure_requestActiveUnlock_dismissKeyguard() {
         // GIVEN shouldTriggerActiveUnlock
         when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
+        when(mFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()).thenReturn(true);
         lockscreenBypassIsNotAllowed();
         when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
                 true);
@@ -2638,7 +1803,8 @@
 
         // WHEN face fails & on the bouncer
         bouncerFullyVisible();
-        mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
+        mFaceAuthenticationListener.getValue().onAuthenticationStatusChanged(
+                new FailedFaceAuthenticationStatus());
 
         // THEN request unlock with a keyguard dismissal
         verify(mTrustManager).reportUserRequestedUnlock(
@@ -2647,54 +1813,6 @@
     }
 
     @Test
-    public void testShouldListenForFace_withAuthSupportPostureConfig_returnsTrue()
-            throws RemoteException {
-        mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_CLOSED;
-        keyguardNotGoingAway();
-        bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        supportsFaceDetection();
-
-        deviceInPostureStateOpened();
-        mTestableLooper.processAllMessages();
-        // Should not listen for face when posture state in DEVICE_POSTURE_OPENED
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
-
-        deviceInPostureStateClosed();
-        mTestableLooper.processAllMessages();
-        // Should listen for face when posture state in DEVICE_POSTURE_CLOSED
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-    }
-
-    @Test
-    public void testShouldListenForFace_withoutAuthSupportPostureConfig_returnsTrue()
-            throws RemoteException {
-        mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_UNKNOWN;
-        keyguardNotGoingAway();
-        bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        supportsFaceDetection();
-
-        deviceInPostureStateClosed();
-        mTestableLooper.processAllMessages();
-        // Whether device in any posture state, always listen for face
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-
-        deviceInPostureStateOpened();
-        mTestableLooper.processAllMessages();
-        // Whether device in any posture state, always listen for face
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
-    }
-
-    @Test
     public void testBatteryChangedIntent_refreshBatteryInfo() {
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(mContext, getBatteryIntent());
 
@@ -2727,8 +1845,7 @@
     }
 
     @Test
-    public void unfoldWakeup_requestActiveUnlock_forceDismissKeyguard()
-            throws RemoteException {
+    public void unfoldWakeup_requestActiveUnlock_forceDismissKeyguard() {
         // GIVEN shouldTriggerActiveUnlock
         keyguardIsVisible();
         when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
@@ -2754,8 +1871,7 @@
     }
 
     @Test
-    public void unfoldWakeup_requestActiveUnlock_noDismissKeyguard()
-            throws RemoteException {
+    public void unfoldWakeup_requestActiveUnlock_noDismissKeyguard() {
         // GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE
         keyguardIsVisible();
         when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
@@ -2781,8 +1897,7 @@
     }
 
     @Test
-    public void unfoldFromPostureChange_requestActiveUnlock_forceDismissKeyguard()
-            throws RemoteException {
+    public void unfoldFromPostureChange_requestActiveUnlock_forceDismissKeyguard() {
         // GIVEN shouldTriggerActiveUnlock
         keyguardIsVisible();
         when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
@@ -2809,8 +1924,7 @@
 
 
     @Test
-    public void unfoldFromPostureChange_requestActiveUnlock_noDismissKeyguard()
-            throws RemoteException {
+    public void unfoldFromPostureChange_requestActiveUnlock_noDismissKeyguard() {
         // GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE
         keyguardIsVisible();
         when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
@@ -2859,44 +1973,6 @@
     }
 
     @Test
-    public void faceAuthenticateOptions_bouncerAuthenticateReason() {
-        // GIVEN the bouncer is fully visible
-        bouncerFullyVisible();
-
-        // WHEN authenticate is called
-        ArgumentCaptor<FaceAuthenticateOptions> captor =
-                ArgumentCaptor.forClass(FaceAuthenticateOptions.class);
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), captor.capture());
-
-        // THEN the authenticate reason is attributed to the bouncer
-        assertThat(captor.getValue().getAuthenticateReason())
-                .isEqualTo(AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN);
-    }
-
-    @Test
-    public void faceAuthenticateOptions_wakingUpAuthenticateReason_powerButtonWakeReason() {
-        // GIVEN keyguard is visible
-        keyguardIsVisible();
-
-        // WHEN device wakes up from the power button
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-
-        // THEN face auth is triggered
-        ArgumentCaptor<FaceAuthenticateOptions> captor =
-                ArgumentCaptor.forClass(FaceAuthenticateOptions.class);
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), captor.capture());
-
-        // THEN the authenticate reason is attributed to the waking
-        assertThat(captor.getValue().getAuthenticateReason())
-                .isEqualTo(AUTHENTICATE_REASON_STARTED_WAKING_UP);
-
-        // THEN the wake reason is attributed to the power button
-        assertThat(captor.getValue().getWakeReason())
-                .isEqualTo(PowerManager.WAKE_REASON_POWER_BUTTON);
-    }
-
-    @Test
     public void testFingerprintSensorProperties() throws RemoteException {
         mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(
                 new ArrayList<>());
@@ -2913,26 +1989,6 @@
     }
 
     @Test
-    public void testFaceSensorProperties() throws RemoteException {
-        // GIVEN no face sensor properties
-        when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true);
-        mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(new ArrayList<>());
-
-        // THEN face is not possible
-        assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible(
-                mSelectedUserInteractor.getSelectedUserId())).isFalse();
-
-        // WHEN there are face sensor properties
-        mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
-
-        // THEN face is possible but face does NOT start listening immediately
-        assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible(
-                mSelectedUserInteractor.getSelectedUserId())).isTrue();
-        verifyFaceAuthenticateNeverCalled();
-        verifyFaceDetectNeverCalled();
-    }
-
-    @Test
     public void testFingerprintListeningStateWhenOccluded() {
         when(mAuthController.isUdfpsSupported()).thenReturn(true);
 
@@ -3014,123 +2070,11 @@
         authCallback.getValue().onEnrollmentsChanged(BiometricAuthenticator.TYPE_FACE);
         mTestableLooper.processAllMessages();
         verify(callback).onBiometricEnrollmentStateChanged(BiometricSourceType.FACE);
-    }
 
-    @Test
-    public void onDisplayOn_nothingHappens() throws RemoteException {
-        // GIVEN
-        keyguardIsVisible();
-        enableStopFaceAuthOnDisplayOff();
-
-        // WHEN the default display state changes to ON
-        triggerDefaultDisplayStateChangeToOn();
-
-        // THEN face auth is NOT started since we rely on STARTED_WAKING_UP to start face auth,
-        // NOT the display on event
-        verifyFaceAuthenticateNeverCalled();
-        verifyFaceDetectNeverCalled();
-    }
-
-    @Test
-    public void onDisplayOff_stopFaceAuth() throws RemoteException {
-        enableStopFaceAuthOnDisplayOff();
-
-        // GIVEN device is listening for face
-        mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+        clearInvocations(callback);
+        mFaceAuthenticationListener.getValue().onAuthEnrollmentStateChanged(false);
         mTestableLooper.processAllMessages();
-        verifyFaceAuthenticateCall();
-
-        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
-        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
-        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
-        mKeyguardUpdateMonitor.registerCallback(callback);
-
-        // WHEN the default display state changes to OFF
-        triggerDefaultDisplayStateChangeToOff();
-
-        // THEN face listening is stopped.
-        verify(faceCancel).cancel();
-        verify(callback).onBiometricRunningStateChanged(
-                eq(false), eq(BiometricSourceType.FACE));
-    }
-
-    @Test
-    public void onDisplayOff_whileAsleep_doesNotStopFaceAuth() throws RemoteException {
-        enableStopFaceAuthOnDisplayOff();
-        when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_ASLEEP);
-
-        // GIVEN device is listening for face
-        mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-        verifyFaceAuthenticateCall();
-
-        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
-        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
-        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
-        mKeyguardUpdateMonitor.registerCallback(callback);
-
-        // WHEN the default display state changes to OFF
-        triggerDefaultDisplayStateChangeToOff();
-
-        // THEN face listening is NOT stopped.
-        verify(faceCancel, never()).cancel();
-        verify(callback, never()).onBiometricRunningStateChanged(
-                eq(false), eq(BiometricSourceType.FACE));
-    }
-
-    @Test
-    public void onDisplayOff_whileWaking_doesNotStopFaceAuth() throws RemoteException {
-        enableStopFaceAuthOnDisplayOff();
-        when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_WAKING);
-
-        // GIVEN device is listening for face
-        mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-        verifyFaceAuthenticateCall();
-
-        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
-        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
-        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
-        mKeyguardUpdateMonitor.registerCallback(callback);
-
-        // WHEN the default display state changes to OFF
-        triggerDefaultDisplayStateChangeToOff();
-
-        // THEN face listening is NOT stopped.
-        verify(faceCancel, never()).cancel();
-        verify(callback, never()).onBiometricRunningStateChanged(
-                eq(false), eq(BiometricSourceType.FACE));
-    }
-
-    private void triggerDefaultDisplayStateChangeToOn() {
-        triggerDefaultDisplayStateChangeTo(true);
-    }
-
-    private void triggerDefaultDisplayStateChangeToOff() {
-        triggerDefaultDisplayStateChangeTo(false);
-    }
-
-    /**
-     * @param on true for Display.STATE_ON, else Display.STATE_OFF
-     */
-    private void triggerDefaultDisplayStateChangeTo(boolean on) {
-        DisplayManagerGlobal displayManagerGlobal = mock(DisplayManagerGlobal.class);
-        DisplayInfo displayInfoWithDisplayState = new DisplayInfo();
-        displayInfoWithDisplayState.state = on ? Display.STATE_ON : Display.STATE_OFF;
-        when(displayManagerGlobal.getDisplayInfo(mDisplayTracker.getDefaultDisplayId()))
-                .thenReturn(displayInfoWithDisplayState);
-        mDisplayTracker.setAllDisplays(new Display[]{
-                new Display(
-                        displayManagerGlobal,
-                        mDisplayTracker.getDefaultDisplayId(),
-                        displayInfoWithDisplayState,
-                        DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
-                )
-        });
-        mDisplayTracker.triggerOnDisplayChanged(mDisplayTracker.getDefaultDisplayId());
+        verify(callback).onBiometricEnrollmentStateChanged(BiometricSourceType.FACE);
     }
 
     private void verifyFingerprintAuthenticateNeverCalled() {
@@ -3151,37 +2095,12 @@
         verify(mFingerprintManager).detectFingerprint(any(), any(), any());
     }
 
-    private void verifyFaceAuthenticateNeverCalled() {
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), any());
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
-    }
-
-    private void verifyFaceAuthenticateCall() {
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), any());
-    }
-
-    private void verifyFaceDetectNeverCalled() {
-        verify(mFaceManager, never()).detectFace(any(), any(), any());
-    }
-
-    private void verifyFaceDetectCall() {
-        verify(mFaceManager).detectFace(any(), any(), any());
-    }
-
     private void userDeviceLockDown() {
         when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
         when(mStrongAuthTracker.getStrongAuthForUser(mCurrentUserId))
                 .thenReturn(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
     }
 
-    private void supportsFaceDetection() throws RemoteException {
-        final boolean isClass3 = !mFaceSensorProperties.isEmpty()
-                && mFaceSensorProperties.get(0).sensorStrength == STRENGTH_STRONG;
-        mFaceSensorProperties =
-                List.of(createFaceSensorProperties(/* supportsFaceDetection = */ true, isClass3));
-        mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
-    }
-
     private void lockscreenBypassIsAllowed() {
         mockCanBypassLockscreen(true);
     }
@@ -3204,38 +2123,20 @@
     }
 
     private void faceAuthLockOut() {
-        mKeyguardUpdateMonitor.mFaceAuthenticationCallback
-                .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
+        when(mFaceAuthInteractor.isLockedOut()).thenReturn(true);
+        mFaceAuthenticationListener.getValue().onAuthenticationStatusChanged(
+                new ErrorFaceAuthenticationStatus(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "",
+                        0L));
     }
 
     private void statusBarShadeIsNotLocked() {
         mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
     }
 
-    private void statusBarShadeIsLocked() {
-        mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
-    }
-
     private void keyguardIsVisible() {
         mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
     }
 
-    private void triggerAuthInterrupt() {
-        mKeyguardUpdateMonitor.onAuthInterruptDetected(true);
-    }
-
-    private void occludingAppRequestsFaceAuth() {
-        mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
-    }
-
-    private void secureCameraLaunched() {
-        mKeyguardUpdateMonitor.onCameraLaunched();
-    }
-
-    private void userCurrentlySwitching() {
-        mKeyguardUpdateMonitor.setSwitchingUser(true);
-    }
-
     private void fingerprintErrorTemporaryLockOut() {
         mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
                 .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
@@ -3245,100 +2146,25 @@
         mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_OPENED);
     }
 
-    private void deviceInPostureStateClosed() {
-        mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_CLOSED);
-    }
-
-    private void successfulFingerprintAuth() {
-        mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
-                .onAuthenticationSucceeded(
-                        new FingerprintManager.AuthenticationResult(null,
-                                null,
-                                mCurrentUserId,
-                                true));
-    }
-
-    private void triggerSuccessfulFaceAuth() {
-        mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
-        verify(mFaceManager).authenticate(any(),
-                any(),
-                mAuthenticationCallbackCaptor.capture(),
-                any(),
-                any());
-        mAuthenticationCallbackCaptor.getValue()
-                .onAuthenticationSucceeded(
-                        new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false));
-    }
-
     private void currentUserIsSystem() {
         when(mUserManager.isSystemUser()).thenReturn(true);
     }
 
-    private void biometricsNotDisabledThroughDevicePolicyManager() {
-        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
-                mSelectedUserInteractor.getSelectedUserId())).thenReturn(0);
-    }
-
     private void biometricsEnabledForCurrentUser() throws RemoteException {
         mBiometricEnabledOnKeyguardCallback.onChanged(true,
                 mSelectedUserInteractor.getSelectedUserId());
     }
 
-    private void biometricsDisabledForCurrentUser() throws RemoteException {
-        mBiometricEnabledOnKeyguardCallback.onChanged(
-                false,
-                mSelectedUserInteractor.getSelectedUserId()
-        );
-    }
-
-    private void primaryAuthRequiredEncrypted() {
-        when(mStrongAuthTracker.getStrongAuthForUser(mSelectedUserInteractor.getSelectedUserId()))
-                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
-        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
-    }
-
-    private void primaryAuthRequiredForWeakBiometricOnly() {
-        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(eq(true))).thenReturn(true);
-        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(eq(false))).thenReturn(false);
-    }
-
     private void primaryAuthNotRequiredByStrongAuthTracker() {
         when(mStrongAuthTracker.getStrongAuthForUser(mSelectedUserInteractor.getSelectedUserId()))
                 .thenReturn(0);
         when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
     }
 
-    private void currentUserDoesNotHaveTrust() {
-        mKeyguardUpdateMonitor.onTrustChanged(
-                false,
-                false,
-                mSelectedUserInteractor.getSelectedUserId(),
-                -1,
-                new ArrayList<>()
-        );
-    }
-
-    private void userNotCurrentlySwitching() {
-        mKeyguardUpdateMonitor.setSwitchingUser(false);
-    }
-
     private void keyguardNotGoingAway() {
         mKeyguardUpdateMonitor.setKeyguardGoingAway(false);
     }
 
-    private void bouncerFullyVisibleAndNotGoingToSleep() {
-        bouncerFullyVisible();
-        deviceNotGoingToSleep();
-    }
-
-    private void deviceNotGoingToSleep() {
-        mKeyguardUpdateMonitor.dispatchFinishedGoingToSleep(/* value doesn't matter */1);
-    }
-
-    private void deviceGoingToSleep() {
-        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(/* value doesn't matter */1);
-    }
-
     private void deviceIsInteractive() {
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
     }
@@ -3347,20 +2173,11 @@
         setKeyguardBouncerVisibility(true);
     }
 
-    private void bouncerNotVisible() {
-        setKeyguardBouncerVisibility(false);
-    }
-
     private void setKeyguardBouncerVisibility(boolean isVisible) {
         mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(isVisible, isVisible);
         mTestableLooper.processAllMessages();
     }
 
-    private void givenUdfpsSupported() {
-        when(mAuthController.isUdfpsSupported()).thenReturn(true);
-        Assert.assertTrue(mKeyguardUpdateMonitor.isUdfpsSupported());
-    }
-
     private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver) {
         BroadcastReceiver.PendingResult pendingResult =
                 new BroadcastReceiver.PendingResult(Activity.RESULT_OK,
@@ -3386,31 +2203,6 @@
         mTestableLooper.processAllMessages();
     }
 
-    private void givenDetectFace() throws RemoteException {
-        // GIVEN bypass is enabled, face detection is supported and primary auth is required
-        lockscreenBypassIsAllowed();
-        supportsFaceDetection();
-        primaryAuthRequiredEncrypted();
-        keyguardIsVisible();
-        // fingerprint is NOT running, UDFPS is NOT supported
-
-        // WHEN the device wakes up
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-    }
-
-    private void enableStopFaceAuthOnDisplayOff() throws RemoteException {
-        cleanupKeyguardUpdateMonitor();
-        clearInvocations(mFaceManager);
-        clearInvocations(mFingerprintManager);
-        clearInvocations(mBiometricManager);
-        clearInvocations(mStatusBarStateController);
-        mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
-        setupBiometrics(mKeyguardUpdateMonitor);
-        when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
-        assertThat(mDisplayTracker.getDisplayCallbacks().size()).isEqualTo(1);
-    }
-
     private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
         int subscription = simInited
                 ? 1/* mock subid=1 */ : SubscriptionManager.PLACEHOLDER_SUBSCRIPTION_ID_BASE;
@@ -3486,11 +2278,10 @@
                     mKeyguardUpdateMonitorLogger, mUiEventLogger, () -> mSessionTracker,
                     mTrustManager, mSubscriptionManager, mUserManager,
                     mDreamManager, mDevicePolicyManager, mSensorPrivacyManager, mTelephonyManager,
-                    mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
+                    mPackageManager, mFingerprintManager, mBiometricManager,
                     mFaceWakeUpTriggersConfig, mDevicePostureController,
                     Optional.of(mInteractiveToAuthProvider),
-                    mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker,
-                    mWakefulness, mSelectedUserInteractor);
+                    mTaskStackChangeListeners, mSelectedUserInteractor, mActivityTaskManager);
             setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index d2f45ae..3d7d701 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -18,7 +18,6 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
-import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
 
@@ -151,7 +150,6 @@
         mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
 
         mFeatureFlags = new FakeFeatureFlags();
-        mFeatureFlags.set(FACE_AUTH_REFACTOR, false);
         mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
         mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
index ea7cc3d..6c91c98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
@@ -100,7 +100,7 @@
 
     @Test
     fun shouldNotShowFaceScanningAnimationIfFaceIsNotEnrolled() {
-        whenever(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
+        whenever(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(false)
         whenever(authController.isShowing).thenReturn(true)
 
         assertThat(underTest.shouldShowFaceScanningAnim()).isFalse()
@@ -108,7 +108,7 @@
 
     @Test
     fun shouldShowFaceScanningAnimationIfBiometricPromptIsShowing() {
-        whenever(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true)
+        whenever(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true)
         whenever(authController.isShowing).thenReturn(true)
 
         assertThat(underTest.shouldShowFaceScanningAnim()).isTrue()
@@ -116,7 +116,7 @@
 
     @Test
     fun shouldShowFaceScanningAnimationIfKeyguardFaceDetectionIsShowing() {
-        whenever(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true)
+        whenever(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true)
         whenever(keyguardUpdateMonitor.isFaceDetectionRunning).thenReturn(true)
 
         assertThat(underTest.shouldShowFaceScanningAnim()).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 11c5d3b..602f3dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -475,6 +475,22 @@
     }
 
     @Test
+    public void testOnAuthenticationFailedInvoked_whenBiometricReEnrollRequired() {
+        showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
+        final int modality = BiometricAuthenticator.TYPE_FACE;
+        mAuthController.onBiometricError(modality,
+                BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL,
+                0 /* vendorCode */);
+
+        verify(mDialog1).onAuthenticationFailed(mModalityCaptor.capture(),
+                mMessageCaptor.capture());
+
+        assertThat(mModalityCaptor.getValue()).isEqualTo(modality);
+        assertThat(mMessageCaptor.getValue()).isEqualTo(mContext.getString(
+                R.string.face_recalibrate_notification_content));
+    }
+
+    @Test
     public void testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected_withPaused() {
         testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected(
                 BiometricConstants.BIOMETRIC_PAUSED_REJECTED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 00ea78f..993dbac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -65,10 +65,7 @@
             .create(
                 test = this,
                 featureFlags =
-                    FakeFeatureFlagsClassicModule {
-                        set(Flags.FACE_AUTH_REFACTOR, false)
-                        set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-                    },
+                    FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
             )
 
     private val detector: AuthDialogPanelInteractionDetector = testComponent.underTest
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 ec17794..86b9b84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
@@ -21,13 +21,10 @@
 import android.view.accessibility.AccessibilityNodeInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.keyguard.FaceAuthApiRequestReason
-import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import org.junit.Assert.assertEquals
 import org.junit.Before
@@ -44,7 +41,6 @@
 @TestableLooper.RunWithLooper
 class FaceAuthAccessibilityDelegateTest : SysuiTestCase() {
 
-    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var hostView: View
     @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
     private lateinit var underTest: FaceAuthAccessibilityDelegate
@@ -55,14 +51,13 @@
         underTest =
             FaceAuthAccessibilityDelegate(
                 context.resources,
-                keyguardUpdateMonitor,
                 faceAuthInteractor,
             )
     }
 
     @Test
     fun shouldListenForFaceTrue_onInitializeAccessibilityNodeInfo_clickActionAdded() {
-        whenever(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(true)
+        whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(true)
 
         // WHEN node is initialized
         val mockedNodeInfo = mock(AccessibilityNodeInfo::class.java)
@@ -81,7 +76,7 @@
 
     @Test
     fun shouldListenForFaceFalse_onInitializeAccessibilityNodeInfo_clickActionNotAdded() {
-        whenever(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(false)
+        whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(false)
 
         // WHEN node is initialized
         val mockedNodeInfo = mock(AccessibilityNodeInfo::class.java)
@@ -94,7 +89,7 @@
 
     @Test
     fun performAccessibilityAction_actionClick_retriesFaceAuth() {
-        whenever(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(true)
+        whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(true)
 
         // WHEN click action is performed
         underTest.performAccessibilityAction(
@@ -103,9 +98,6 @@
             null
         )
 
-        // THEN retry face auth
-        verify(keyguardUpdateMonitor)
-            .requestFaceAuth(eq(FaceAuthApiRequestReason.ACCESSIBILITY_ACTION))
         verify(faceAuthInteractor).onAccessibilityAction()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 5f0d4d4..f5b6f14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -36,13 +36,13 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
@@ -107,7 +107,6 @@
     @Mock private lateinit var udfpsView: UdfpsView
     @Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy
     @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
-    @Mock private lateinit var featureFlags: FeatureFlags
     @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
     @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@@ -123,47 +122,52 @@
     @Before
     fun setup() {
         whenever(inflater.inflate(R.layout.udfps_view, null, false))
-            .thenReturn(udfpsView)
+                .thenReturn(udfpsView)
         whenever(inflater.inflate(R.layout.udfps_bp_view, null))
-            .thenReturn(mock(UdfpsBpView::class.java))
+                .thenReturn(mock(UdfpsBpView::class.java))
         whenever(inflater.inflate(R.layout.udfps_keyguard_view_legacy, null))
-            .thenReturn(mUdfpsKeyguardViewLegacy)
+                .thenReturn(mUdfpsKeyguardViewLegacy)
         whenever(inflater.inflate(R.layout.udfps_fpm_empty_view, null))
-            .thenReturn(mock(UdfpsFpmEmptyView::class.java))
+                .thenReturn(mock(UdfpsFpmEmptyView::class.java))
     }
 
     private fun withReason(
-        @ShowReason reason: Int,
-        isDebuggable: Boolean = false,
-        block: () -> Unit
+            @ShowReason reason: Int,
+            isDebuggable: Boolean = false,
+            enableDeviceEntryUdfpsRefactor: Boolean = false,
+            block: () -> Unit,
     ) {
+        if (enableDeviceEntryUdfpsRefactor) {
+            mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+        } else {
+            mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+        }
         controllerOverlay = UdfpsControllerOverlay(
-            context,
-            inflater,
-            windowManager,
-            accessibilityManager,
-            statusBarStateController,
-            statusBarKeyguardViewManager,
-            keyguardUpdateMonitor,
-            dialogManager,
-            dumpManager,
-            transitionController,
-            configurationController,
-            keyguardStateController,
-            unlockedScreenOffAnimationController,
-            udfpsDisplayMode,
-            REQUEST_ID,
-            reason,
-            controllerCallback,
-            onTouch,
-            activityLaunchAnimator,
-            featureFlags,
-            primaryBouncerInteractor,
-            alternateBouncerInteractor,
-            isDebuggable,
-            udfpsKeyguardAccessibilityDelegate,
-            keyguardTransitionInteractor,
-            mSelectedUserInteractor,
+                context,
+                inflater,
+                windowManager,
+                accessibilityManager,
+                statusBarStateController,
+                statusBarKeyguardViewManager,
+                keyguardUpdateMonitor,
+                dialogManager,
+                dumpManager,
+                transitionController,
+                configurationController,
+                keyguardStateController,
+                unlockedScreenOffAnimationController,
+                udfpsDisplayMode,
+                REQUEST_ID,
+                reason,
+                controllerCallback,
+                onTouch,
+                activityLaunchAnimator,
+                primaryBouncerInteractor,
+                alternateBouncerInteractor,
+                isDebuggable,
+                udfpsKeyguardAccessibilityDelegate,
+                keyguardTransitionInteractor,
+                mSelectedUserInteractor,
         )
         block()
     }
@@ -185,12 +189,12 @@
         val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT)
         val overlayBounds = Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
         overlayParams = UdfpsOverlayParams(
-            sensorBounds,
-            overlayBounds,
-            DISPLAY_WIDTH,
-            DISPLAY_HEIGHT,
-            scaleFactor = 1f,
-            rotation
+                sensorBounds,
+                overlayBounds,
+                DISPLAY_WIDTH,
+                DISPLAY_HEIGHT,
+                scaleFactor = 1f,
+                rotation
         )
         block()
     }
@@ -200,8 +204,8 @@
         withReason(REASON_AUTH_BP) {
             controllerOverlay.show(udfpsController, overlayParams)
             verify(windowManager).addView(
-                eq(controllerOverlay.overlayView),
-                layoutParamsCaptor.capture()
+                    eq(controllerOverlay.getTouchOverlay()),
+                    layoutParamsCaptor.capture()
             )
 
             // ROTATION_0 is the native orientation. Sensor should stay in the top left corner.
@@ -218,8 +222,8 @@
         withReason(REASON_AUTH_BP) {
             controllerOverlay.show(udfpsController, overlayParams)
             verify(windowManager).addView(
-                eq(controllerOverlay.overlayView),
-                layoutParamsCaptor.capture()
+                    eq(controllerOverlay.getTouchOverlay()),
+                    layoutParamsCaptor.capture()
             )
 
             // ROTATION_180 is not supported. Sensor should stay in the top left corner.
@@ -236,8 +240,8 @@
         withReason(REASON_AUTH_BP) {
             controllerOverlay.show(udfpsController, overlayParams)
             verify(windowManager).addView(
-                eq(controllerOverlay.overlayView),
-                layoutParamsCaptor.capture()
+                    eq(controllerOverlay.getTouchOverlay()),
+                    layoutParamsCaptor.capture()
             )
 
             // Sensor should be in the bottom left corner in ROTATION_90.
@@ -254,8 +258,8 @@
         withReason(REASON_AUTH_BP) {
             controllerOverlay.show(udfpsController, overlayParams)
             verify(windowManager).addView(
-                eq(controllerOverlay.overlayView),
-                layoutParamsCaptor.capture()
+                    eq(controllerOverlay.getTouchOverlay()),
+                    layoutParamsCaptor.capture()
             )
 
             // Sensor should be in the top right corner in ROTATION_270.
@@ -270,7 +274,7 @@
     private fun showUdfpsOverlay() {
         val didShow = controllerOverlay.show(udfpsController, overlayParams)
 
-        verify(windowManager).addView(eq(controllerOverlay.overlayView), any())
+        verify(windowManager).addView(eq(controllerOverlay.getTouchOverlay()), any())
         verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode))
         verify(udfpsView).animationViewController = any()
         verify(udfpsView).addView(any())
@@ -278,7 +282,7 @@
         assertThat(didShow).isTrue()
         assertThat(controllerOverlay.isShowing).isTrue()
         assertThat(controllerOverlay.isHiding).isFalse()
-        assertThat(controllerOverlay.overlayView).isNotNull()
+        assertThat(controllerOverlay.getTouchOverlay()).isNotNull()
     }
 
     @Test
@@ -295,14 +299,14 @@
 
     private fun hideUdfpsOverlay() {
         val didShow = controllerOverlay.show(udfpsController, overlayParams)
-        val view = controllerOverlay.overlayView
+        val view = controllerOverlay.getTouchOverlay()
         val didHide = controllerOverlay.hide()
 
         verify(windowManager).removeView(eq(view))
 
         assertThat(didShow).isTrue()
         assertThat(didHide).isTrue()
-        assertThat(controllerOverlay.overlayView).isNull()
+        assertThat(controllerOverlay.getTouchOverlay()).isNull()
         assertThat(controllerOverlay.animationViewController).isNull()
         assertThat(controllerOverlay.isShowing).isFalse()
         assertThat(controllerOverlay.isHiding).isTrue()
@@ -348,8 +352,8 @@
 
             controllerOverlay.show(udfpsController, overlayParams)
             verify(windowManager).addView(
-                eq(controllerOverlay.overlayView),
-                layoutParamsCaptor.capture()
+                    eq(controllerOverlay.getTouchOverlay()),
+                    layoutParamsCaptor.capture()
             )
 
             // Layout params should use sensor bounds
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index c8c400d..e2cab29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -73,6 +73,7 @@
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
@@ -286,6 +287,7 @@
         // Create a fake background executor.
         mBiometricExecutor = new FakeExecutor(new FakeSystemClock());
 
+        mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
         initUdfpsController(mOpticalProps);
     }
 
@@ -304,7 +306,6 @@
                 mStatusBarKeyguardViewManager,
                 mDumpManager,
                 mKeyguardUpdateMonitor,
-                mFeatureFlags,
                 mFalsingManager,
                 mPowerManager,
                 mAccessibilityManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 9bff88b..79f0625 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -34,6 +34,7 @@
 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
@@ -106,6 +107,7 @@
                 FakeTrustRepository(),
                 testScope.backgroundScope,
                 mSelectedUserInteractor,
+                mock(KeyguardFaceAuthInteractor::class.java),
             )
         mAlternateBouncerInteractor =
             AlternateBouncerInteractor(
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 b48bc1d2..094616f 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
@@ -43,6 +43,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.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
@@ -62,6 +63,7 @@
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito
+import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -122,6 +124,7 @@
                 fakeTrustRepository,
                 testScope.backgroundScope,
                 mSelectedUserInteractor,
+                mock(KeyguardFaceAuthInteractor::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 d6aa9ac..37a093e 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
@@ -16,7 +16,6 @@
 
 package com.android.systemui.bouncer.domain.interactor
 
-import android.hardware.biometrics.BiometricSourceType
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.testing.TestableResources
@@ -35,6 +34,7 @@
 import com.android.systemui.classifier.FalsingCollector
 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.statusbar.policy.KeyguardStateController
@@ -72,6 +72,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
     private lateinit var mainHandler: FakeHandler
     private lateinit var underTest: PrimaryBouncerInteractor
     private lateinit var resources: TestableResources
@@ -103,6 +104,7 @@
                 trustRepository,
                 testScope.backgroundScope,
                 mSelectedUserInteractor,
+                faceAuthInteractor,
             )
         whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
         whenever(repository.primaryBouncerShow.value).thenReturn(false)
@@ -423,10 +425,7 @@
         mainHandler.setMode(FakeHandler.Mode.QUEUEING)
 
         // GIVEN bouncer should be delayed due to face auth
-        whenever(keyguardStateController.isFaceEnrolled).thenReturn(true)
-        whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
-            .thenReturn(true)
-        whenever(keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()).thenReturn(true)
+        whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(true)
 
         // WHEN bouncer show is requested
         underTest.show(true)
@@ -444,15 +443,12 @@
     }
 
     @Test
-    fun noDelayBouncer_biometricsAllowed_postureDoesNotAllowFaceAuth() {
+    fun noDelayBouncer_faceAuthNotAllowed() {
         mainHandler.setMode(FakeHandler.Mode.QUEUEING)
 
         // GIVEN bouncer should not be delayed because device isn't in the right posture for
         // face auth
-        whenever(keyguardStateController.isFaceEnrolled).thenReturn(true)
-        whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
-            .thenReturn(true)
-        whenever(keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()).thenReturn(false)
+        whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(false)
 
         // WHEN bouncer show is requested
         underTest.show(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
index d1b120e..bdf5041 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.coroutines.collectLastValue
 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
@@ -53,6 +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
     private val mainHandler = FakeHandler(Looper.getMainLooper())
     private lateinit var underTest: PrimaryBouncerInteractor
 
@@ -75,6 +77,7 @@
                 Mockito.mock(TrustRepository::class.java),
                 TestScope().backgroundScope,
                 mSelectedUserInteractor,
+                faceAuthInteractor,
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 2cc8f0a..90e0c19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.coroutines.collectValues
 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
@@ -61,6 +62,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
 
     lateinit var bouncerInteractor: PrimaryBouncerInteractor
     private val mainHandler = FakeHandler(Looper.getMainLooper())
@@ -86,6 +88,7 @@
                 Mockito.mock(TrustRepository::class.java),
                 TestScope().backgroundScope,
                 mSelectedUserInteractor,
+                faceAuthInteractor,
             )
         underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 6c990e45..40c9432 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -30,7 +30,6 @@
 import android.view.SurfaceControlViewHost
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
 import com.android.systemui.SystemUIAppComponentFactoryBase
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogLaunchAnimator
@@ -51,6 +50,7 @@
 import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRendererFactory
 import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
@@ -173,7 +173,6 @@
             FakeFeatureFlags().apply {
                 set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
                 set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
-                set(Flags.FACE_AUTH_REFACTOR, true)
             }
         underTest.interactor =
             KeyguardQuickAffordanceInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index f0ff77e..852f9a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -52,7 +52,6 @@
         MockitoAnnotations.initMocks(this)
         featureFlags.set(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK, true)
         featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, true)
-        featureFlags.set(Flags.FACE_AUTH_REFACTOR, false)
         powerInteractor = PowerInteractorFactory.create().powerInteractor
         keyguardRepository.setDozeAmount(0f)
         keyguardRepository.setKeyguardGoingAway(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 0b148d1..45aca17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -58,7 +58,6 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
 import com.android.systemui.flags.Flags.KEYGUARD_WM_STATE_REFACTOR
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
@@ -188,11 +187,7 @@
         biometricSettingsRepository = FakeBiometricSettingsRepository()
         deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
         trustRepository = FakeTrustRepository()
-        featureFlags =
-            FakeFeatureFlags().apply {
-                set(FACE_AUTH_REFACTOR, true)
-                set(KEYGUARD_WM_STATE_REFACTOR, false)
-            }
+        featureFlags = FakeFeatureFlags().apply { set(KEYGUARD_WM_STATE_REFACTOR, false) }
 
         powerRepository = FakePowerRepository()
         powerInteractor =
@@ -790,21 +785,19 @@
         }
 
     @Test
-    fun everythingWorksWithFaceAuthRefactorFlagDisabled() =
+    fun everythingEmitsADefaultValueAndDoesNotErrorOut() =
         testScope.runTest {
-            featureFlags.set(FACE_AUTH_REFACTOR, false)
-
             underTest = createDeviceEntryFaceAuthRepositoryImpl()
             initCollectors()
 
             // Collecting any flows exposed in the public API doesn't throw any error
-            authStatus()
-            detectStatus()
-            authRunning()
-            bypassEnabled()
-            lockedOut()
-            canFaceAuthRun()
-            authenticated()
+            assertThat(authStatus()).isNull()
+            assertThat(detectStatus()).isNull()
+            assertThat(authRunning()).isNotNull()
+            assertThat(bypassEnabled()).isNotNull()
+            assertThat(lockedOut()).isNotNull()
+            assertThat(canFaceAuthRun()).isNotNull()
+            assertThat(authenticated()).isNotNull()
         }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index f2de8ca..b3e8fed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -41,8 +41,6 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dump.logcatLogBuffer
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
@@ -109,8 +107,6 @@
         val scheduler = TestCoroutineScheduler()
         val dispatcher = StandardTestDispatcher(scheduler)
         testScope = TestScope(dispatcher)
-        val featureFlags = FakeFeatureFlags()
-        featureFlags.set(Flags.FACE_AUTH_REFACTOR, true)
         bouncerRepository = FakeKeyguardBouncerRepository()
         faceAuthRepository = FakeDeviceEntryFaceAuthRepository()
         keyguardTransitionRepository = FakeKeyguardTransitionRepository()
@@ -135,21 +131,24 @@
                 testScope.backgroundScope,
                 dispatcher,
                 faceAuthRepository,
-                PrimaryBouncerInteractor(
-                    bouncerRepository,
-                    mock(BouncerView::class.java),
-                    mock(Handler::class.java),
-                    mock(KeyguardStateController::class.java),
-                    mock(KeyguardSecurityModel::class.java),
-                    mock(PrimaryBouncerCallbackInteractor::class.java),
-                    mock(FalsingCollector::class.java),
-                    mock(DismissCallbackRegistry::class.java),
-                    context,
-                    keyguardUpdateMonitor,
-                    FakeTrustRepository(),
-                    testScope.backgroundScope,
-                    mSelectedUserInteractor,
-                ),
+                {
+                    PrimaryBouncerInteractor(
+                        bouncerRepository,
+                        mock(BouncerView::class.java),
+                        mock(Handler::class.java),
+                        mock(KeyguardStateController::class.java),
+                        mock(KeyguardSecurityModel::class.java),
+                        mock(PrimaryBouncerCallbackInteractor::class.java),
+                        mock(FalsingCollector::class.java),
+                        mock(DismissCallbackRegistry::class.java),
+                        context,
+                        keyguardUpdateMonitor,
+                        FakeTrustRepository(),
+                        testScope.backgroundScope,
+                        mSelectedUserInteractor,
+                        underTest,
+                    )
+                },
                 AlternateBouncerInteractor(
                     mock(StatusBarStateController::class.java),
                     mock(KeyguardStateController::class.java),
@@ -161,7 +160,6 @@
                     testScope.backgroundScope,
                 ),
                 keyguardTransitionInteractor,
-                featureFlags,
                 FaceAuthenticationLogger(logcatLogBuffer("faceAuthBuffer")),
                 keyguardUpdateMonitor,
                 fakeDeviceEntryFingerprintAuthRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index ad2ec72..706f94e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -24,8 +24,6 @@
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory
@@ -54,7 +52,6 @@
     private val repository = testUtils.keyguardRepository
     private val sceneInteractor = testUtils.sceneInteractor()
     private val commandQueue = FakeCommandQueue()
-    private val featureFlags = FakeFeatureFlagsClassic().apply { set(FACE_AUTH_REFACTOR, true) }
     private val bouncerRepository = FakeKeyguardBouncerRepository()
     private val configurationRepository = FakeConfigurationRepository()
     private val shadeRepository = FakeShadeRepository()
@@ -66,7 +63,6 @@
             repository = repository,
             commandQueue = commandQueue,
             powerInteractor = PowerInteractorFactory.create().powerInteractor,
-            featureFlags = featureFlags,
             sceneContainerFlags = testUtils.sceneContainerFlags,
             bouncerRepository = bouncerRepository,
             configurationRepository = configurationRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index fe474fa..66c8a22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dock.DockManagerFake
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
@@ -302,10 +301,7 @@
                 dumpManager = mock(),
                 userHandle = UserHandle.SYSTEM,
             )
-        val featureFlags =
-            FakeFeatureFlags().apply {
-                set(Flags.FACE_AUTH_REFACTOR, true)
-            }
+        val featureFlags = FakeFeatureFlags()
         val testDispatcher = StandardTestDispatcher()
         testScope = TestScope(testDispatcher)
         underTest =
@@ -357,7 +353,7 @@
                 }
 
             underTest.onQuickAffordanceTriggered(
-                configKey = "${KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()}::${key}",
+                configKey = "${KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()}::$key",
                 expandable = expandable,
                 slotId = "",
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 347d580..bc4bae0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.common.shared.model.ContentDescription
@@ -31,7 +30,6 @@
 import com.android.systemui.dock.DockManager
 import com.android.systemui.dock.DockManagerFake
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
@@ -48,6 +46,7 @@
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
@@ -102,9 +101,11 @@
         overrideResource(
             R.array.config_keyguardQuickAffordanceDefaults,
             arrayOf(
-                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START + ":" +
+                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START +
+                    ":" +
                     BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
-                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END + ":" +
+                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END +
+                    ":" +
                     BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
             )
         )
@@ -168,10 +169,7 @@
                 dumpManager = mock(),
                 userHandle = UserHandle.SYSTEM,
             )
-        featureFlags =
-            FakeFeatureFlags().apply {
-                set(Flags.FACE_AUTH_REFACTOR, true)
-            }
+        featureFlags = FakeFeatureFlags()
 
         val withDeps =
             KeyguardInteractorFactory.create(
@@ -216,9 +214,8 @@
             assertThat(collectedValue())
                 .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
             val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible
-            assertThat(visibleModel.configKey).isEqualTo(
-                "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${configKey}"
-            )
+            assertThat(visibleModel.configKey)
+                .isEqualTo("${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::$configKey")
             assertThat(visibleModel.icon).isEqualTo(ICON)
             assertThat(visibleModel.icon.contentDescription)
                 .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
@@ -243,9 +240,8 @@
             assertThat(collectedValue())
                 .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
             val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible
-            assertThat(visibleModel.configKey).isEqualTo(
-                "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END}::${configKey}"
-            )
+            assertThat(visibleModel.configKey)
+                .isEqualTo("${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END}::$configKey")
             assertThat(visibleModel.icon).isEqualTo(ICON)
             assertThat(visibleModel.icon.contentDescription)
                 .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
@@ -364,9 +360,8 @@
             assertThat(collectedValue())
                 .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
             val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible
-            assertThat(visibleModel.configKey).isEqualTo(
-                "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${configKey}"
-            )
+            assertThat(visibleModel.configKey)
+                .isEqualTo("${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::$configKey")
             assertThat(visibleModel.icon).isEqualTo(ICON)
             assertThat(visibleModel.icon.contentDescription)
                 .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index bf23bf8..bf6d5c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -121,11 +121,7 @@
 
         whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
 
-        featureFlags =
-            FakeFeatureFlags().apply {
-                set(Flags.FACE_AUTH_REFACTOR, true)
-                set(Flags.KEYGUARD_WM_STATE_REFACTOR, false)
-            }
+        featureFlags = FakeFeatureFlags().apply { set(Flags.KEYGUARD_WM_STATE_REFACTOR, false) }
 
         keyguardInteractor = createKeyguardInteractor()
 
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 91e1705..1f245f1 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
@@ -32,7 +32,6 @@
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -103,7 +102,7 @@
         keyguardRepository = FakeKeyguardRepository()
         bouncerRepository = FakeKeyguardBouncerRepository()
         configurationRepository = FakeConfigurationRepository()
-        featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
+        featureFlags = FakeFeatureFlags()
         trustRepository = FakeTrustRepository()
         powerRepository = FakePowerRepository()
         powerInteractor =
@@ -147,7 +146,8 @@
                     keyguardUpdateMonitor,
                     trustRepository,
                     testScope.backgroundScope,
-                    mSelectedUserInteractor
+                    mSelectedUserInteractor,
+                    keyguardFaceAuthInteractor = mock(),
                 ),
                 AlternateBouncerInteractor(
                     statusBarStateController = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 16d072e..87eee1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -25,8 +25,6 @@
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -64,7 +62,6 @@
     private lateinit var bouncerRepository: KeyguardBouncerRepository
     private lateinit var keyguardRepository: FakeKeyguardRepository
     private lateinit var fakeCommandQueue: FakeCommandQueue
-    private lateinit var featureFlags: FakeFeatureFlags
     private lateinit var burnInInteractor: BurnInInteractor
     private lateinit var shadeRepository: FakeShadeRepository
     private lateinit var keyguardInteractor: KeyguardInteractor
@@ -80,8 +77,7 @@
         MockitoAnnotations.initMocks(this)
         testScope = TestScope()
         configRepository = FakeConfigurationRepository()
-        featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
-        KeyguardInteractorFactory.create(featureFlags = featureFlags).let {
+        KeyguardInteractorFactory.create().let {
             keyguardInteractor = it.keyguardInteractor
             keyguardRepository = it.repository
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 32d28a3..1584be0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.animation.Expandable
@@ -52,6 +51,7 @@
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
@@ -116,9 +116,11 @@
         overrideResource(
             R.array.config_keyguardQuickAffordanceDefaults,
             arrayOf(
-                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START + ":" +
+                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START +
+                    ":" +
                     BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
-                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END + ":" +
+                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END +
+                    ":" +
                     BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
             )
         )
@@ -138,7 +140,6 @@
         biometricSettingsRepository = FakeBiometricSettingsRepository()
         val featureFlags =
             FakeFeatureFlags().apply {
-                set(Flags.FACE_AUTH_REFACTOR, true)
                 set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false)
                 set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false)
             }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 25d1419..67c4e26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -22,14 +22,13 @@
 import android.os.UserHandle
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
+import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dock.DockManagerFake
-import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
@@ -49,6 +48,7 @@
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
@@ -129,7 +129,6 @@
 
         val featureFlags =
             FakeFeatureFlags().apply {
-                set(Flags.FACE_AUTH_REFACTOR, true)
                 set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false)
                 set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false)
             }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index a838684..e6d6cf2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -32,9 +32,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
-import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
@@ -109,9 +107,7 @@
 
         mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
 
-        val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
-
-        val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+        val withDeps = KeyguardInteractorFactory.create()
         keyguardInteractor = withDeps.keyguardInteractor
         repository = withDeps.repository
         configurationRepository = withDeps.configurationRepository
@@ -135,7 +131,6 @@
                 deviceEntryInteractor =
                     mock { whenever(isBypassEnabled).thenReturn(MutableStateFlow(false)) },
                 dozeParameters = mock(),
-                featureFlags,
                 keyguardInteractor,
                 keyguardTransitionInteractor,
                 notificationsKeyguardInteractor =
@@ -347,8 +342,7 @@
         DaggerKeyguardRootViewModelTestWithFakes_TestComponent.factory()
             .create(
                 test = this,
-                featureFlags =
-                    FakeFeatureFlagsClassicModule { set(Flags.FACE_AUTH_REFACTOR, true) },
+                featureFlags = FakeFeatureFlagsClassicModule(),
                 mocks =
                     TestMocksModule(
                         dozeParameters = dozeParams,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
index c50be04..2314c83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
-import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
 import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -94,10 +93,7 @@
             .create(
                 test = this,
                 featureFlags =
-                    FakeFeatureFlagsClassicModule {
-                        set(FACE_AUTH_REFACTOR, true)
-                        set(FULL_SCREEN_USER_SWITCHER, true)
-                    },
+                    FakeFeatureFlagsClassicModule { set(FULL_SCREEN_USER_SWITCHER, true) },
                 mocks = TestMocksModule(),
             )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 26704da..baac513 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -85,10 +85,7 @@
             .create(
                 test = this,
                 featureFlags =
-                    FakeFeatureFlagsClassicModule {
-                        set(Flags.FACE_AUTH_REFACTOR, true)
-                        set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-                    },
+                    FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
                 mocks = TestMocksModule(),
             )
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index ff3135a6..29d8f08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -85,10 +85,7 @@
             .create(
                 test = this,
                 featureFlags =
-                    FakeFeatureFlagsClassicModule {
-                        set(Flags.FACE_AUTH_REFACTOR, true)
-                        set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-                    },
+                    FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
                 mocks = TestMocksModule(),
             )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index 8afd8e4..049e4e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -86,10 +86,7 @@
             .create(
                 test = this,
                 featureFlags =
-                    FakeFeatureFlagsClassicModule {
-                        set(Flags.FACE_AUTH_REFACTOR, true)
-                        set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-                    },
+                    FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
                 mocks = TestMocksModule(),
             )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
index 5058b16..6512290 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -23,8 +23,6 @@
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -54,7 +52,6 @@
     private lateinit var configRepository: FakeConfigurationRepository
     private lateinit var bouncerRepository: KeyguardBouncerRepository
     private lateinit var keyguardRepository: FakeKeyguardRepository
-    private lateinit var featureFlags: FakeFeatureFlags
     private lateinit var shadeRepository: FakeShadeRepository
     private lateinit var keyguardInteractor: KeyguardInteractor
 
@@ -67,16 +64,12 @@
         overrideResource(com.android.systemui.res.R.dimen.lock_icon_padding, defaultPadding)
         testScope = TestScope()
         shadeRepository = FakeShadeRepository()
-        featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
-        KeyguardInteractorFactory.create(
-                featureFlags = featureFlags,
-            )
-            .also {
-                keyguardInteractor = it.keyguardInteractor
-                keyguardRepository = it.repository
-                configRepository = it.configurationRepository
-                bouncerRepository = it.bouncerRepository
-            }
+        KeyguardInteractorFactory.create().also {
+            keyguardInteractor = it.keyguardInteractor
+            keyguardRepository = it.repository
+            configRepository = it.configurationRepository
+            bouncerRepository = it.bouncerRepository
+        }
         val udfpsKeyguardInteractor =
             UdfpsKeyguardInteractor(
                 configRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
index f039f53..95b2fe5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
@@ -24,8 +24,6 @@
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -59,7 +57,6 @@
     private lateinit var bouncerRepository: KeyguardBouncerRepository
     private lateinit var keyguardRepository: FakeKeyguardRepository
     private lateinit var fakeCommandQueue: FakeCommandQueue
-    private lateinit var featureFlags: FakeFeatureFlags
     private lateinit var transitionRepository: FakeKeyguardTransitionRepository
     private lateinit var shadeRepository: FakeShadeRepository
 
@@ -75,14 +72,12 @@
         keyguardRepository = FakeKeyguardRepository()
         bouncerRepository = FakeKeyguardBouncerRepository()
         fakeCommandQueue = FakeCommandQueue()
-        featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
         bouncerRepository = FakeKeyguardBouncerRepository()
         transitionRepository = FakeKeyguardTransitionRepository()
         shadeRepository = FakeShadeRepository()
         val keyguardInteractor =
             KeyguardInteractorFactory.create(
                     repository = keyguardRepository,
-                    featureFlags = featureFlags,
                 )
                 .keyguardInteractor
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
index c1805db..848a94b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
@@ -23,8 +23,6 @@
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
@@ -75,7 +73,6 @@
     private lateinit var keyguardInteractor: KeyguardInteractor
     private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
     private lateinit var shadeRepository: FakeShadeRepository
-    private lateinit var featureFlags: FakeFeatureFlags
 
     @Before
     fun setUp() {
@@ -83,16 +80,12 @@
         testScope = TestScope()
         transitionRepository = FakeKeyguardTransitionRepository()
         shadeRepository = FakeShadeRepository()
-        featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
-        KeyguardInteractorFactory.create(
-                featureFlags = featureFlags,
-            )
-            .also {
-                keyguardInteractor = it.keyguardInteractor
-                keyguardRepository = it.repository
-                configRepository = it.configurationRepository
-                bouncerRepository = it.bouncerRepository
-            }
+        KeyguardInteractorFactory.create().also {
+            keyguardInteractor = it.keyguardInteractor
+            keyguardRepository = it.repository
+            configRepository = it.configurationRepository
+            bouncerRepository = it.bouncerRepository
+        }
 
         val transitionInteractor =
             KeyguardTransitionInteractorFactory.create(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 5c325ae..42e27ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -19,7 +19,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
@@ -39,14 +38,11 @@
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class QuickSettingsSceneViewModelTest : SysuiTestCase() {
@@ -90,15 +86,8 @@
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
 
-        val authenticationInteractor = utils.authenticationInteractor()
-
         underTest =
             QuickSettingsSceneViewModel(
-                deviceEntryInteractor =
-                    utils.deviceEntryInteractor(
-                        authenticationInteractor = authenticationInteractor,
-                        sceneInteractor = sceneInteractor,
-                    ),
                 shadeHeaderViewModel = shadeHeaderViewModel,
                 qsSceneAdapter = qsFlexiglassAdapter,
                 notifications = utils.notificationsPlaceholderViewModel(),
@@ -106,32 +95,6 @@
     }
 
     @Test
-    fun onContentClicked_deviceUnlocked_switchesToGone() =
-        testScope.runTest {
-            val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.deviceEntryRepository.setUnlocked(true)
-            runCurrent()
-
-            underTest.onContentClicked()
-
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
-        }
-
-    @Test
-    fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
-        testScope.runTest {
-            val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.deviceEntryRepository.setUnlocked(false)
-            runCurrent()
-
-            underTest.onContentClicked()
-
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
-        }
-
-    @Test
     fun destinationsNotCustomizing() =
         testScope.runTest {
             val destinations by collectLastValue(underTest.destinationScenes)
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 722fb2c..36b4435 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -31,7 +31,6 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
@@ -56,7 +55,6 @@
 import androidx.constraintlayout.widget.ConstraintSet;
 import androidx.test.filters.SmallTest;
 
-import com.android.keyguard.FaceAuthApiRequestReason;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
@@ -1075,33 +1073,6 @@
         mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
 
         verify(mKeyguardFaceAuthInteractor).onNotificationPanelClicked();
-        verify(mUpdateMonitor).requestFaceAuth(
-                FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED);
-    }
-
-    @Test
-    public void onEmptySpaceClicked_whenDozingAndOnKeyguard_doesNotRequestFaceAuth() {
-        StatusBarStateController.StateListener statusBarStateListener =
-                mNotificationPanelViewController.getStatusBarStateListener();
-        statusBarStateListener.onStateChanged(KEYGUARD);
-        mNotificationPanelViewController.setDozing(true, false);
-
-        // This sets the dozing state that is read when onMiddleClicked is eventually invoked.
-        mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
-        mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
-
-        verify(mUpdateMonitor, never()).requestFaceAuth(anyString());
-    }
-
-    @Test
-    public void onEmptySpaceClicked_whenStatusBarShadeLocked_doesNotRequestFaceAuth() {
-        StatusBarStateController.StateListener statusBarStateListener =
-                mNotificationPanelViewController.getStatusBarStateListener();
-        statusBarStateListener.onStateChanged(SHADE_LOCKED);
-
-        mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
-
-        verify(mUpdateMonitor, never()).requestFaceAuth(anyString());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 8403ac5..39b306b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -188,7 +188,6 @@
                 keyguardRepository,
                 new FakeCommandQueue(),
                 powerInteractor,
-                featureFlags,
                 sceneContainerFlags,
                 new FakeKeyguardBouncerRepository(),
                 configurationRepository,
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 d89491c..0587633 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -65,6 +65,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.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -97,6 +98,7 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.test.TestScope
@@ -111,9 +113,8 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import java.util.Optional
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -265,6 +266,7 @@
                             FakeTrustRepository(),
                             testScope.backgroundScope,
                             mSelectedUserInteractor,
+                            mock(KeyguardFaceAuthInteractor::class.java)
                         ),
                     facePropertyRepository = FakeFacePropertyRepository(),
                     deviceEntryFingerprintAuthRepository =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 9c8816c..29b1366 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -251,6 +251,7 @@
                             FakeTrustRepository(),
                             testScope.backgroundScope,
                             mSelectedUserInteractor,
+                            mock(),
                         ),
                     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 bff47f1..1dbb297 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -224,7 +224,6 @@
                 mKeyguardRepository,
                 new FakeCommandQueue(),
                 powerInteractor,
-                featureFlags,
                 sceneContainerFlags,
                 new FakeKeyguardBouncerRepository(),
                 configurationRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
index 61e4370..65e0fa1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
@@ -105,10 +105,7 @@
             .create(
                 test = this,
                 featureFlags =
-                    FakeFeatureFlagsClassicModule {
-                        set(Flags.FACE_AUTH_REFACTOR, false)
-                        set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-                    },
+                    FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
                 mocks =
                     TestMocksModule(
                         dozeParameters = dozeParameters,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
index 92eb6ed..6e6e438 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
@@ -86,10 +86,7 @@
             .create(
                 test = this,
                 featureFlags =
-                    FakeFeatureFlagsClassicModule {
-                        set(Flags.FACE_AUTH_REFACTOR, false)
-                        set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-                    },
+                    FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
                 mocks =
                     TestMocksModule(
                         dozeParameters = dozeParameters,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
index 729f3f8..565e20a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -91,10 +91,7 @@
             .create(
                 test = this,
                 featureFlags =
-                    FakeFeatureFlagsClassicModule {
-                        set(Flags.FACE_AUTH_REFACTOR, false)
-                        set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-                    },
+                    FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
                 mocks =
                     TestMocksModule(
                         dozeParameters = dozeParameters,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
index 63e46d1..459040a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
@@ -18,7 +18,6 @@
 
 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
 
-import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
 import static com.android.systemui.flags.Flags.KEYGUARD_TALKBACK_FIX;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
 import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
@@ -56,7 +55,6 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.FaceHelpMessageDeferral;
@@ -72,6 +70,7 @@
 import com.android.systemui.keyguard.util.IndicationHelper;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
@@ -249,7 +248,6 @@
         mFlags = new FakeFeatureFlags();
         mFlags.set(KEYGUARD_TALKBACK_FIX, true);
         mFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
-        mFlags.set(FACE_AUTH_REFACTOR, false);
         mController = new KeyguardIndicationController(
                 mContext,
                 mTestableLooper.getLooper(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index dd3ac92..aa53558 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -479,7 +479,7 @@
         createController();
 
         // GIVEN face has already unlocked the device
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithFace(anyInt())).thenReturn(true);
+        when(mKeyguardUpdateMonitor.isCurrentUserUnlockedWithFace()).thenReturn(true);
 
         String message = "A message";
         mController.setVisible(true);
@@ -586,7 +586,7 @@
         createController();
         String message = mContext.getString(R.string.keyguard_retry);
         when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
-        when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true);
+        when(mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled()).thenReturn(true);
         when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(false);
 
         mController.setVisible(true);
@@ -602,7 +602,7 @@
         String message = mContext.getString(R.string.keyguard_retry);
         when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
         when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
-        when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true);
+        when(mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled()).thenReturn(true);
 
         mController.setVisible(true);
         mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 8fa7cd2..7546dfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -66,8 +66,8 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -93,88 +93,98 @@
         whenever(interactionJankMonitor.end(anyInt())).thenReturn(true)
 
         uiEventLogger = UiEventLoggerFake()
-        controller = object : StatusBarStateControllerImpl(
-            uiEventLogger,
-            interactionJankMonitor,
-            mock(),
-            { shadeInteractor }
-        ) {
-            override fun createDarkAnimator(): ObjectAnimator { return mockDarkAnimator }
-        }
+        controller =
+            object :
+                StatusBarStateControllerImpl(
+                    uiEventLogger,
+                    interactionJankMonitor,
+                    mock(),
+                    { shadeInteractor }
+                ) {
+                override fun createDarkAnimator(): ObjectAnimator {
+                    return mockDarkAnimator
+                }
+            }
 
-        val powerInteractor = PowerInteractor(
-            FakePowerRepository(),
-            FalsingCollectorFake(),
-            mock(),
-            controller)
+        val powerInteractor =
+            PowerInteractor(FakePowerRepository(), FalsingCollectorFake(), mock(), controller)
         val keyguardRepository = FakeKeyguardRepository()
         val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
         val featureFlags = FakeFeatureFlagsClassic()
         val shadeRepository = FakeShadeRepository()
         val sceneContainerFlags = FakeSceneContainerFlags()
         val configurationRepository = FakeConfigurationRepository()
-        val keyguardInteractor = KeyguardInteractor(
-            keyguardRepository,
-            FakeCommandQueue(),
-            powerInteractor,
-            featureFlags,
-            sceneContainerFlags,
-            FakeKeyguardBouncerRepository(),
-            configurationRepository,
-            shadeRepository,
-            utils::sceneInteractor)
-        val keyguardTransitionInteractor = KeyguardTransitionInteractor(
-            testScope.backgroundScope,
-            keyguardTransitionRepository,
-            { keyguardInteractor },
-            { fromLockscreenTransitionInteractor },
-            { fromPrimaryBouncerTransitionInteractor })
-        fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor(
-            keyguardTransitionRepository,
-            keyguardTransitionInteractor,
-            testScope.backgroundScope,
-            keyguardInteractor,
-            featureFlags,
-            shadeRepository,
-            powerInteractor,
-            {
-                InWindowLauncherUnlockAnimationInteractor(
-                    InWindowLauncherUnlockAnimationRepository(),
-                    testScope,
-                    keyguardTransitionInteractor,
-                    { FakeKeyguardSurfaceBehindRepository() },
-                    mock(),
-                )
-            })
-        fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor(
-            keyguardTransitionRepository,
-            keyguardTransitionInteractor,
-            testScope.backgroundScope,
-            keyguardInteractor,
-            featureFlags,
-            mock(),
-            mock(),
-            powerInteractor)
-        shadeInteractor = ShadeInteractorImpl(
-            testScope.backgroundScope,
-            FakeDeviceProvisioningRepository(),
-            FakeDisableFlagsRepository(),
-            mock(),
-            keyguardRepository,
-            keyguardTransitionInteractor,
-            powerInteractor,
-            FakeUserSetupRepository(),
-            mock(),
-            ShadeInteractorLegacyImpl(
-                testScope.backgroundScope,
+        val keyguardInteractor =
+            KeyguardInteractor(
                 keyguardRepository,
-                SharedNotificationContainerInteractor(
-                    configurationRepository,
-                    mContext,
-                    ResourcesSplitShadeStateController()),
+                FakeCommandQueue(),
+                powerInteractor,
+                sceneContainerFlags,
+                FakeKeyguardBouncerRepository(),
+                configurationRepository,
                 shadeRepository,
+                utils::sceneInteractor
             )
-        )
+        val keyguardTransitionInteractor =
+            KeyguardTransitionInteractor(
+                testScope.backgroundScope,
+                keyguardTransitionRepository,
+                { keyguardInteractor },
+                { fromLockscreenTransitionInteractor },
+                { fromPrimaryBouncerTransitionInteractor }
+            )
+        fromLockscreenTransitionInteractor =
+            FromLockscreenTransitionInteractor(
+                keyguardTransitionRepository,
+                keyguardTransitionInteractor,
+                testScope.backgroundScope,
+                keyguardInteractor,
+                featureFlags,
+                shadeRepository,
+                powerInteractor,
+                {
+                    InWindowLauncherUnlockAnimationInteractor(
+                        InWindowLauncherUnlockAnimationRepository(),
+                        testScope,
+                        keyguardTransitionInteractor,
+                        { FakeKeyguardSurfaceBehindRepository() },
+                        mock(),
+                    )
+                }
+            )
+        fromPrimaryBouncerTransitionInteractor =
+            FromPrimaryBouncerTransitionInteractor(
+                keyguardTransitionRepository,
+                keyguardTransitionInteractor,
+                testScope.backgroundScope,
+                keyguardInteractor,
+                featureFlags,
+                mock(),
+                mock(),
+                powerInteractor
+            )
+        shadeInteractor =
+            ShadeInteractorImpl(
+                testScope.backgroundScope,
+                FakeDeviceProvisioningRepository(),
+                FakeDisableFlagsRepository(),
+                mock(),
+                keyguardRepository,
+                keyguardTransitionInteractor,
+                powerInteractor,
+                FakeUserSetupRepository(),
+                mock(),
+                ShadeInteractorLegacyImpl(
+                    testScope.backgroundScope,
+                    keyguardRepository,
+                    SharedNotificationContainerInteractor(
+                        configurationRepository,
+                        mContext,
+                        ResourcesSplitShadeStateController()
+                    ),
+                    shadeRepository,
+                )
+            )
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index d1a46fc..057dcb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -62,7 +62,7 @@
     private val ongoingCallRepository = OngoingCallRepository()
 
     private val underTest =
-        StatusBarModeRepositoryImpl(
+        StatusBarModePerDisplayRepositoryImpl(
                 testScope.backgroundScope,
                 DISPLAY_ID,
                 commandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index 10d4c62..b3fc25c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -93,7 +93,6 @@
                 test = this,
                 featureFlags =
                     FakeFeatureFlagsClassicModule {
-                        setDefault(Flags.FACE_AUTH_REFACTOR)
                         set(Flags.FULL_SCREEN_USER_SWITCHER, value = false)
                     },
                 mocks =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index e264fc0..7415645 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -105,7 +105,6 @@
                 test = this,
                 featureFlags =
                     FakeFeatureFlagsClassicModule {
-                        setDefault(Flags.FACE_AUTH_REFACTOR)
                         set(Flags.FULL_SCREEN_USER_SWITCHER, value = false)
                     },
                 mocks =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
index 1c7fd56..7361f6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
@@ -18,7 +18,6 @@
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE
@@ -37,7 +36,7 @@
 @RunWith(AndroidTestingRunner::class)
 class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() {
     init {
-        setFlagsRule.disableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR)
+        mSetFlagsRule.disableFlags(VisualInterruptionRefactor.FLAG_NAME)
     }
 
     override val provider by lazy {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index df6f0d7..d2c046c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -18,7 +18,6 @@
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK
@@ -30,7 +29,7 @@
 @RunWith(AndroidTestingRunner::class)
 class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionProviderTestBase() {
     init {
-        setFlagsRule.enableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR)
+        mSetFlagsRule.enableFlags(VisualInterruptionRefactor.FLAG_NAME)
     }
 
     override val provider by lazy {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 7babff5..2ac0cb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -44,7 +44,6 @@
 import android.hardware.display.FakeAmbientDisplayConfiguration
 import android.os.Looper
 import android.os.PowerManager
-import android.platform.test.flag.junit.SetFlagsRule
 import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
 import android.provider.Settings.Global.HEADS_UP_OFF
 import android.provider.Settings.Global.HEADS_UP_ON
@@ -84,15 +83,10 @@
 import junit.framework.Assert.assertTrue
 import org.junit.Assert.assertEquals
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.mockito.Mockito.`when` as whenever
 
 abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
-    @JvmField
-    @Rule
-    val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
-
     private val fakeLogBuffer =
         LogBuffer(
             name = "FakeLog",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index e61b4f8..051a4c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -135,7 +135,7 @@
         MockitoAnnotations.initMocks(this);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
         when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
-        when(mKeyguardStateController.isFaceEnrolled()).thenReturn(true);
+        when(mKeyguardStateController.isFaceEnrolledAndEnabled()).thenReturn(true);
         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
         when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean()))
                 .thenReturn(true);
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 4b1c7e8..4422764 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
@@ -157,6 +157,7 @@
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper;
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -347,6 +348,8 @@
         mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
         when(mDozeParameters.getAlwaysOn()).thenReturn(true);
         mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+        // TODO: b/312476335 - Update to check flag and instantiate old or new implementation.
+        mSetFlagsRule.disableFlags(VisualInterruptionRefactor.FLAG_NAME);
 
         IThermalService thermalService = mock(IThermalService.class);
         mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
index bd0dbee..91cbc32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
@@ -86,7 +86,7 @@
         featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
 
         whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true)
-        whenever(keyguardStateController.isFaceEnrolled).thenReturn(true)
+        whenever(keyguardStateController.isFaceEnrolledAndEnabled).thenReturn(true)
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 361df1c..62a2bc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -60,7 +60,6 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.SceneTestUtils;
@@ -166,7 +165,6 @@
                 mKeyguardRepository,
                 mCommandQueue,
                 PowerInteractorFactory.create().getPowerInteractor(),
-                mFeatureFlags,
                 mSceneTestUtils.getSceneContainerFlags(),
                 new FakeKeyguardBouncerRepository(),
                 new FakeConfigurationRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java
index 287ebba..bde2243 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java
@@ -54,7 +54,7 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
-public class LightsOutNotifControllerTest extends SysuiTestCase {
+public class LegacyLightsOutNotifControllerTest extends SysuiTestCase {
     private static final int LIGHTS_ON = 0;
     private static final int LIGHTS_OUT = APPEARANCE_LOW_PROFILE_BARS;
 
@@ -68,7 +68,7 @@
     @Captor private ArgumentCaptor<CommandQueue.Callbacks> mCallbacksCaptor;
 
     private View mLightsOutView;
-    private LightsOutNotifController mLightsOutNotifController;
+    private LegacyLightsOutNotifController mLightsOutNotifController;
     private int mDisplayId;
     private Observer<Boolean> mHaActiveNotifsObserver;
     private CommandQueue.Callbacks mCallbacks;
@@ -83,7 +83,7 @@
         when(mNotifLiveDataStore.getHasActiveNotifs()).thenReturn(mHasActiveNotifs);
         when(mHasActiveNotifs.getValue()).thenReturn(false);
 
-        mLightsOutNotifController = new LightsOutNotifController(
+        mLightsOutNotifController = new LegacyLightsOutNotifController(
                 mLightsOutView,
                 mWindowManager,
                 mNotifLiveDataStore,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
index c45ecf3..f6419a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -121,7 +121,7 @@
                 new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, secondBounds)
         );
 
-        mStatusBarModeRepository.getStatusBarAppearance().setValue(
+        mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
                 new StatusBarAppearance(
                         StatusBarMode.TRANSPARENT,
                         STATUS_BAR_BOUNDS,
@@ -142,7 +142,7 @@
                 new AppearanceRegion(0 /* appearance */, secondBounds)
         );
 
-        mStatusBarModeRepository.getStatusBarAppearance().setValue(
+        mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
                 new StatusBarAppearance(
                         StatusBarMode.TRANSPARENT,
                         STATUS_BAR_BOUNDS,
@@ -165,7 +165,7 @@
                 new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, secondBounds)
         );
 
-        mStatusBarModeRepository.getStatusBarAppearance().setValue(
+        mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
                 new StatusBarAppearance(
                         StatusBarMode.TRANSPARENT,
                         STATUS_BAR_BOUNDS,
@@ -190,7 +190,7 @@
                 new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, thirdBounds)
         );
 
-        mStatusBarModeRepository.getStatusBarAppearance().setValue(
+        mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
                 new StatusBarAppearance(
                         StatusBarMode.TRANSPARENT,
                         STATUS_BAR_BOUNDS,
@@ -214,7 +214,7 @@
                 new AppearanceRegion(0 /* appearance */, secondBounds)
         );
 
-        mStatusBarModeRepository.getStatusBarAppearance().setValue(
+        mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
                 new StatusBarAppearance(
                         StatusBarMode.TRANSPARENT,
                         STATUS_BAR_BOUNDS,
@@ -231,7 +231,7 @@
                 new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, new Rect(0, 0, 1, 1))
         );
 
-        mStatusBarModeRepository.getStatusBarAppearance().setValue(
+        mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
                 new StatusBarAppearance(
                         StatusBarMode.TRANSPARENT,
                         STATUS_BAR_BOUNDS,
@@ -249,7 +249,7 @@
                 new AppearanceRegion(0, new Rect(0, 0, 1, 1))
         );
 
-        mStatusBarModeRepository.getStatusBarAppearance().setValue(
+        mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
                 new StatusBarAppearance(
                         StatusBarMode.TRANSPARENT,
                         STATUS_BAR_BOUNDS,
@@ -266,7 +266,7 @@
                 new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, new Rect(0, 0, 1, 1))
         );
 
-        mStatusBarModeRepository.getStatusBarAppearance().setValue(
+        mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
                 new StatusBarAppearance(
                         StatusBarMode.TRANSPARENT,
                         STATUS_BAR_BOUNDS,
@@ -276,7 +276,7 @@
         reset(mStatusBarIconController);
 
         // WHEN the same appearance regions but different status bar mode is sent
-        mStatusBarModeRepository.getStatusBarAppearance().setValue(
+        mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
                 new StatusBarAppearance(
                         StatusBarMode.LIGHTS_OUT_TRANSPARENT,
                         STATUS_BAR_BOUNDS,
@@ -298,7 +298,7 @@
                 /* start= */ new Rect(0, 0, 10, 10),
                 /* end= */ new Rect(0, 0, 20, 20));
 
-        mStatusBarModeRepository.getStatusBarAppearance().setValue(
+        mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
                 new StatusBarAppearance(
                         StatusBarMode.TRANSPARENT,
                         startingBounds,
@@ -311,7 +311,7 @@
         BoundsPair newBounds = new BoundsPair(
                 /* start= */ new Rect(0, 0, 30, 30),
                 /* end= */ new Rect(0, 0, 40, 40));
-        mStatusBarModeRepository.getStatusBarAppearance().setValue(
+        mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
                 new StatusBarAppearance(
                         StatusBarMode.TRANSPARENT,
                         newBounds,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt
new file mode 100644
index 0000000..5a0e13d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+@SmallTest
+class LightsOutInteractorTest : SysuiTestCase() {
+
+    private val statusBarModeRepository = FakeStatusBarModeRepository()
+    private val interactor: LightsOutInteractor = LightsOutInteractor(statusBarModeRepository)
+
+    @Test
+    fun isLowProfile_lightsOutStatusBarMode_false() = runTest {
+        statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.LIGHTS_OUT
+
+        val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID))
+
+        assertThat(actual).isTrue()
+    }
+
+    @Test
+    fun isLowProfile_lightsOutTransparentStatusBarMode_true() = runTest {
+        statusBarModeRepository.defaultDisplay.statusBarMode.value =
+            StatusBarMode.LIGHTS_OUT_TRANSPARENT
+
+        val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID))
+
+        assertThat(actual).isTrue()
+    }
+
+    @Test
+    fun isLowProfile_transparentStatusBarMode_false() = runTest {
+        statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT
+
+        val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID))
+
+        assertThat(actual).isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 0b87fe8..17c2938 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -46,7 +46,6 @@
 import com.android.systemui.common.ui.ConfigurationState;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlagsClassic;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.LogcatEchoTracker;
 import com.android.systemui.plugins.DarkIconDispatcher;
@@ -695,7 +694,6 @@
                 mLocationPublisher,
                 mMockNotificationAreaController,
                 mShadeExpansionStateManager,
-                mock(FeatureFlagsClassic.class),
                 mStatusBarIconController,
                 mIconManagerFactory,
                 mCollapsedStatusBarViewModel,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 49de512..7b73528c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -549,7 +549,7 @@
     @Test
     fun fullscreenIsTrue_chipStillClickable() {
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-        statusBarModeRepository.isInFullscreenMode.value = true
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
         testScope.runCurrent()
 
         assertThat(chipView.hasOnClickListeners()).isTrue()
@@ -559,7 +559,7 @@
 
     @Test
     fun callStartedInImmersiveMode_swipeGestureCallbackAdded() {
-        statusBarModeRepository.isInFullscreenMode.value = true
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
         testScope.runCurrent()
 
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -570,7 +570,7 @@
 
     @Test
     fun callStartedNotInImmersiveMode_swipeGestureCallbackNotAdded() {
-        statusBarModeRepository.isInFullscreenMode.value = false
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
         testScope.runCurrent()
 
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -583,7 +583,7 @@
     fun transitionToImmersiveMode_swipeGestureCallbackAdded() {
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
 
-        statusBarModeRepository.isInFullscreenMode.value = true
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
         testScope.runCurrent()
 
         verify(mockSwipeStatusBarAwayGestureHandler)
@@ -592,11 +592,11 @@
 
     @Test
     fun transitionOutOfImmersiveMode_swipeGestureCallbackRemoved() {
-        statusBarModeRepository.isInFullscreenMode.value = true
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
         reset(mockSwipeStatusBarAwayGestureHandler)
 
-        statusBarModeRepository.isInFullscreenMode.value = false
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
         testScope.runCurrent()
 
         verify(mockSwipeStatusBarAwayGestureHandler)
@@ -605,7 +605,7 @@
 
     @Test
     fun callEndedWhileInImmersiveMode_swipeGestureCallbackRemoved() {
-        statusBarModeRepository.isInFullscreenMode.value = true
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
         testScope.runCurrent()
         val ongoingCallNotifEntry = createOngoingCallNotifEntry()
         notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index 688f739..09dc1e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -17,49 +17,77 @@
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
 import androidx.test.filters.SmallTest
+import com.android.systemui.CoroutineTestScopeModule
+import com.android.systemui.Flags
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectValues
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-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
+import com.android.systemui.runTest
+import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 class CollapsedStatusBarViewModelImplTest : SysuiTestCase() {
 
-    private lateinit var underTest: CollapsedStatusBarViewModel
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+            ]
+    )
+    interface TestComponent : SysUITestComponent<CollapsedStatusBarViewModelImpl> {
+        val statusBarModeRepository: FakeStatusBarModeRepository
+        val activeNotificationListRepository: ActiveNotificationListRepository
+        val keyguardTransitionRepository: FakeKeyguardTransitionRepository
 
-    private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
-    private lateinit var testScope: TestScope
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                testScope: CoroutineTestScopeModule,
+            ): TestComponent
+        }
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    private val testComponent: TestComponent =
+        DaggerCollapsedStatusBarViewModelImplTest_TestComponent.factory()
+            .create(
+                test = this,
+                testScope = CoroutineTestScopeModule(TestScope(UnconfinedTestDispatcher())),
+            )
 
     @Before
     fun setUp() {
-        testScope = TestScope(UnconfinedTestDispatcher())
-
-        keyguardTransitionRepository = FakeKeyguardTransitionRepository()
-        val interactor =
-            KeyguardTransitionInteractorFactory.create(
-                    scope = TestScope().backgroundScope,
-                    repository = keyguardTransitionRepository,
-                )
-                .keyguardTransitionInteractor
-        underTest = CollapsedStatusBarViewModelImpl(interactor, testScope.backgroundScope)
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_LIVE_DATA_STORE_REFACTOR)
     }
 
     @Test
     fun isTransitioningFromLockscreenToOccluded_started_isTrue() =
-        testScope.runTest {
-            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
+        testComponent.runTest {
+            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope)
 
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
@@ -77,8 +105,8 @@
 
     @Test
     fun isTransitioningFromLockscreenToOccluded_running_isTrue() =
-        testScope.runTest {
-            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
+        testComponent.runTest {
+            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope)
 
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
@@ -96,13 +124,13 @@
 
     @Test
     fun isTransitioningFromLockscreenToOccluded_finished_isFalse() =
-        testScope.runTest {
-            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
+        testComponent.runTest {
+            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope)
 
             keyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
                 to = KeyguardState.OCCLUDED,
-                this.testScheduler,
+                testScope.testScheduler,
             )
 
             assertThat(underTest.isTransitioningFromLockscreenToOccluded.value).isFalse()
@@ -112,8 +140,8 @@
 
     @Test
     fun isTransitioningFromLockscreenToOccluded_canceled_isFalse() =
-        testScope.runTest {
-            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
+        testComponent.runTest {
+            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope)
 
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
@@ -131,8 +159,8 @@
 
     @Test
     fun isTransitioningFromLockscreenToOccluded_irrelevantTransition_isFalse() =
-        testScope.runTest {
-            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
+        testComponent.runTest {
+            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope)
 
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
@@ -150,8 +178,8 @@
 
     @Test
     fun isTransitioningFromLockscreenToOccluded_followsRepoUpdates() =
-        testScope.runTest {
-            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
+        testComponent.runTest {
+            val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope)
 
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
@@ -182,7 +210,7 @@
 
     @Test
     fun transitionFromLockscreenToDreamStartedEvent_started_emitted() =
-        testScope.runTest {
+        testComponent.runTest {
             val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent)
 
             keyguardTransitionRepository.sendTransitionStep(
@@ -199,7 +227,7 @@
 
     @Test
     fun transitionFromLockscreenToDreamStartedEvent_startedMultiple_emittedMultiple() =
-        testScope.runTest {
+        testComponent.runTest {
             val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent)
 
             keyguardTransitionRepository.sendTransitionStep(
@@ -234,7 +262,7 @@
 
     @Test
     fun transitionFromLockscreenToDreamStartedEvent_startedThenRunning_emittedOnlyOne() =
-        testScope.runTest {
+        testComponent.runTest {
             val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent)
 
             keyguardTransitionRepository.sendTransitionStep(
@@ -283,7 +311,7 @@
 
     @Test
     fun transitionFromLockscreenToDreamStartedEvent_irrelevantTransition_notEmitted() =
-        testScope.runTest {
+        testComponent.runTest {
             val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent)
 
             keyguardTransitionRepository.sendTransitionStep(
@@ -300,7 +328,7 @@
 
     @Test
     fun transitionFromLockscreenToDreamStartedEvent_irrelevantTransitionState_notEmitted() =
-        testScope.runTest {
+        testComponent.runTest {
             val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent)
 
             keyguardTransitionRepository.sendTransitionStep(
@@ -317,4 +345,65 @@
 
             assertThat(emissions).isEmpty()
         }
+
+    @Test
+    fun areNotificationsLightsOut_lowProfileWithNotifications_true() =
+        testComponent.runTest {
+            statusBarModeRepository.defaultDisplay.statusBarMode.value =
+                StatusBarMode.LIGHTS_OUT_TRANSPARENT
+            activeNotificationListRepository.activeNotifications.value =
+                activeNotificationsStore(testNotifications)
+
+            val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID))
+
+            assertThat(actual).isTrue()
+        }
+
+    @Test
+    fun areNotificationsLightsOut_lowProfileWithoutNotifications_false() =
+        testComponent.runTest {
+            statusBarModeRepository.defaultDisplay.statusBarMode.value =
+                StatusBarMode.LIGHTS_OUT_TRANSPARENT
+            activeNotificationListRepository.activeNotifications.value =
+                activeNotificationsStore(emptyList())
+
+            val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID))
+
+            assertThat(actual).isFalse()
+        }
+
+    @Test
+    fun areNotificationsLightsOut_defaultStatusBarModeWithoutNotifications_false() =
+        testComponent.runTest {
+            statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT
+            activeNotificationListRepository.activeNotifications.value =
+                activeNotificationsStore(emptyList())
+
+            val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID))
+
+            assertThat(actual).isFalse()
+        }
+
+    @Test
+    fun areNotificationsLightsOut_defaultStatusBarModeWithNotifications_false() =
+        testComponent.runTest {
+            statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT
+            activeNotificationListRepository.activeNotifications.value =
+                activeNotificationsStore(testNotifications)
+
+            val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID))
+
+            assertThat(actual).isFalse()
+        }
+
+    private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
+        ActiveNotificationsStore.Builder()
+            .apply { notifications.forEach(::addIndividualNotif) }
+            .build()
+
+    private val testNotifications =
+        listOf(
+            activeNotificationModel(key = "notif1"),
+            activeNotificationModel(key = "notif2"),
+        )
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
index 88587b2..bc50f79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
@@ -16,11 +16,20 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 
 class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel {
+    private val areNotificationLightsOut = MutableStateFlow(false)
+
     override val isTransitioningFromLockscreenToOccluded = MutableStateFlow(false)
 
     override val transitionFromLockscreenToDreamStartedEvent = MutableSharedFlow<Unit>()
+
+    override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut
+
+    fun setNotificationLightsOut(lightsOut: Boolean) {
+        areNotificationLightsOut.value = lightsOut
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index 5c960b6..01dad38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -100,16 +100,16 @@
     public void testFaceAuthEnrolleddChanged_calledWhenFaceEnrollmentStateChanges() {
         KeyguardStateController.Callback callback = mock(KeyguardStateController.Callback.class);
 
-        when(mKeyguardUpdateMonitor.isFaceEnrolled(anyInt())).thenReturn(false);
+        when(mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled()).thenReturn(false);
         verify(mKeyguardUpdateMonitor).registerCallback(mUpdateCallbackCaptor.capture());
         mKeyguardStateController.addCallback(callback);
-        assertThat(mKeyguardStateController.isFaceEnrolled()).isFalse();
+        assertThat(mKeyguardStateController.isFaceEnrolledAndEnabled()).isFalse();
 
-        when(mKeyguardUpdateMonitor.isFaceEnrolled(anyInt())).thenReturn(true);
+        when(mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled()).thenReturn(true);
         mUpdateCallbackCaptor.getValue().onBiometricEnrollmentStateChanged(
                 BiometricSourceType.FACE);
 
-        assertThat(mKeyguardStateController.isFaceEnrolled()).isTrue();
+        assertThat(mKeyguardStateController.isFaceEnrolledAndEnabled()).isTrue();
         verify(callback).onFaceEnrolledChanged();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index 96db09e..59bf9f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -55,7 +54,6 @@
             keyguardRepository,
             mock<CommandQueue>(),
             PowerInteractorFactory.create().powerInteractor,
-            FakeFeatureFlagsClassic(),
             sceneTestUtils.sceneContainerFlags,
             FakeKeyguardBouncerRepository(),
             FakeConfigurationRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index 7f990a4..f924134 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -26,7 +26,6 @@
 import com.android.internal.util.LatencyTracker
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
@@ -106,8 +105,7 @@
             onActionStarted.run()
         }
 
-        val featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
-        val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+        val withDeps = KeyguardInteractorFactory.create(featureFlags = FakeFeatureFlags())
         val keyguardInteractor = withDeps.keyguardInteractor
         keyguardRepository = withDeps.repository
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 0d78ae9..abfff34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -23,8 +23,6 @@
 import android.provider.Settings
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.user.data.model.SelectedUserModel
 import com.android.systemui.user.data.model.SelectionStatus
@@ -322,8 +320,6 @@
         }
 
     private fun create(scope: CoroutineScope = TestCoroutineScope()): UserRepositoryImpl {
-        val featureFlags = FakeFeatureFlags()
-        featureFlags.set(FACE_AUTH_REFACTOR, true)
         return UserRepositoryImpl(
             appContext = context,
             manager = manager,
@@ -332,7 +328,6 @@
             backgroundDispatcher = IMMEDIATE,
             globalSettings = globalSettings,
             tracker = tracker,
-            featureFlags = featureFlags,
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index 017eefe..bf851eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -121,8 +121,6 @@
         )
 
         utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
-        utils.featureFlags.set(Flags.FACE_AUTH_REFACTOR, true)
-
         spyContext = spy(context)
         keyguardReply = KeyguardInteractorFactory.create(featureFlags = utils.featureFlags)
         keyguardRepository = keyguardReply.repository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 7041eab..d1870b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -233,11 +233,7 @@
         }
 
     private fun viewModel(): StatusBarUserChipViewModel {
-        val featureFlags =
-            FakeFeatureFlags().apply {
-                set(Flags.FULL_SCREEN_USER_SWITCHER, false)
-                set(Flags.FACE_AUTH_REFACTOR, true)
-            }
+        val featureFlags = FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
         runBlocking {
             userRepository.setUserInfos(listOf(USER_0))
             userRepository.setSelectedUserInfo(USER_0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 686f492..b7b24f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -147,11 +147,7 @@
                 resetOrExitSessionReceiver = resetOrExitSessionReceiver,
             )
 
-        val featureFlags =
-            FakeFeatureFlags().apply {
-                set(Flags.FULL_SCREEN_USER_SWITCHER, false)
-                set(Flags.FACE_AUTH_REFACTOR, true)
-            }
+        val featureFlags = FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
         val reply = KeyguardInteractorFactory.create(featureFlags = featureFlags)
         keyguardRepository = reply.repository
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index df7609c..ca167ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -146,6 +146,7 @@
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
@@ -366,6 +367,9 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        // TODO: b/312476335 - Update to check flag and instantiate old or new implementation.
+        mSetFlagsRule.disableFlags(VisualInterruptionRefactor.FLAG_NAME);
+
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             doReturn(true).when(mTransitions).isRegistered();
         }
@@ -411,7 +415,6 @@
                 keyguardRepository,
                 new FakeCommandQueue(),
                 powerInteractor,
-                featureFlags,
                 sceneContainerFlags,
                 new FakeKeyguardBouncerRepository(),
                 configurationRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/android/app/ActivityManagerKosmos.kt
similarity index 82%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/android/app/ActivityManagerKosmos.kt
index a780763..f9c920a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/app/ActivityManagerKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package android.app
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.activityManager by Kosmos.Fixture { mock<ActivityManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/android/app/admin/DevicePolicyManagerKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/android/app/admin/DevicePolicyManagerKosmos.kt
index a780763..b284ac0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/app/admin/DevicePolicyManagerKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package android.app.admin
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.devicePolicyManager by Kosmos.Fixture { mock<DevicePolicyManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt
index a780763..f96c508 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package android.content
 
+import com.android.systemui.SysuiTestableContext
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.testableContext: SysuiTestableContext by Kosmos.Fixture { testCase.context }
+var Kosmos.applicationContext: Context by Kosmos.Fixture { testableContext }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/res/ResourcesKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/android/content/res/ResourcesKosmos.kt
index a780763..5686764 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/content/res/ResourcesKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package android.content.res
 
+import android.content.applicationContext
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.mainResources: Resources by Kosmos.Fixture { applicationContext.resources }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/android/os/UserManagerKosmos.kt
similarity index 83%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/android/os/UserManagerKosmos.kt
index a780763..c936b91 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/os/UserManagerKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package android.os
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.userManager by Kosmos.Fixture { mock<UserManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt
similarity index 79%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt
index a780763..34c0a79 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package android.view
 
+import android.content.applicationContext
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.layoutInflater: LayoutInflater by
+    Kosmos.Fixture { LayoutInflater.from(applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/internal/logging/UiEventLoggerKosmos.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/internal/logging/UiEventLoggerKosmos.kt
index a780763..9059da2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/internal/logging/UiEventLoggerKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.internal.logging
 
+import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.uiEventLogger: UiEventLogger by Kosmos.Fixture { uiEventLoggerFake }
+val Kosmos.uiEventLoggerFake by Kosmos.Fixture { UiEventLoggerFake() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardSecurityModelKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardSecurityModelKosmos.kt
index a780763..fadcecc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardSecurityModelKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.keyguard
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.keyguardSecurityModel by Kosmos.Fixture { mock<KeyguardSecurityModel>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUpdateMonitorKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUpdateMonitorKosmos.kt
index a780763..b32cbe6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUpdateMonitorKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.keyguard
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.keyguardUpdateMonitor by Kosmos.Fixture { mock<KeyguardUpdateMonitor>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResetOrExitSessionReceiverKosmos.kt
similarity index 77%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/GuestResetOrExitSessionReceiverKosmos.kt
index a780763..4c4cfd5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResetOrExitSessionReceiverKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.guestResetOrExitSessionReceiver by
+    Kosmos.Fixture { mock<GuestResetOrExitSessionReceiver>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResumeSessionReceiverKosmos.kt
similarity index 79%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/GuestResumeSessionReceiverKosmos.kt
index a780763..a9855ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResumeSessionReceiverKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.guestResumeSessionReceiver by Kosmos.Fixture { mock<GuestResumeSessionReceiver>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryKosmos.kt
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryKosmos.kt
index a780763..ea93e94 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryKosmos.kt
@@ -14,8 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.authentication.data.repository
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import kotlinx.coroutines.test.currentTime
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.authenticationRepository: AuthenticationRepository by
+    Kosmos.Fixture { fakeAuthenticationRepository }
+val Kosmos.fakeAuthenticationRepository by
+    Kosmos.Fixture { FakeAuthenticationRepository { testScope.currentTime } }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
new file mode 100644
index 0000000..060ca4c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.authentication.domain.interactor
+
+import com.android.systemui.authentication.data.repository.authenticationRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.user.data.repository.userRepository
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.authenticationInteractor by
+    Kosmos.Fixture {
+        AuthenticationInteractor(
+            applicationScope = applicationCoroutineScope,
+            repository = authenticationRepository,
+            backgroundDispatcher = testDispatcher,
+            userRepository = userRepository,
+            clock = fakeSystemClock,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryKosmos.kt
similarity index 70%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryKosmos.kt
index a780763..2a87074 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.bouncer.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.keyguardBouncerRepository: KeyguardBouncerRepository by
+    Kosmos.Fixture { fakeKeyguardBouncerRepository }
+val Kosmos.fakeKeyguardBouncerRepository by Kosmos.Fixture { FakeKeyguardBouncerRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingCollectorKosmos.kt
similarity index 85%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingCollectorKosmos.kt
index a780763..3a72d11 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingCollectorKosmos.kt
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.classifier
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.falsingCollector by Kosmos.Fixture { FalsingCollectorFake() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
similarity index 65%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
index a780763..86a8ae5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
@@ -14,8 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.common.ui
 
+import android.content.applicationContext
+import android.view.layoutInflater
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.configurationController
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.configurationState: ConfigurationState by
+    Kosmos.Fixture {
+        ConfigurationState(configurationController, applicationContext, layoutInflater)
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryKosmos.kt
similarity index 71%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryKosmos.kt
index a780763..77b8bd4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.common.ui.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.configurationRepository: ConfigurationRepository by
+    Kosmos.Fixture { fakeConfigurationRepository }
+val Kosmos.fakeConfigurationRepository by Kosmos.Fixture { FakeConfigurationRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
index b27926c..faacce6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
@@ -65,6 +65,7 @@
                 widgetRepository,
                 mediaRepository,
                 smartspaceRepository,
+                withDeps.keyguardInteractor,
                 appWidgetHost,
                 editWidgetsActivityStarter,
             ),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryKosmos.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryKosmos.kt
index a780763..3da0681 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.deviceentry.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.deviceEntryRepository: DeviceEntryRepository by
+    Kosmos.Fixture { fakeDeviceEntryRepository }
+val Kosmos.fakeDeviceEntryRepository by Kosmos.Fixture { FakeDeviceEntryRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
new file mode 100644
index 0000000..b600b50
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+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.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.sceneContainerFlags
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.deviceEntryInteractor by
+    Kosmos.Fixture {
+        DeviceEntryInteractor(
+            applicationScope = applicationCoroutineScope,
+            repository = deviceEntryRepository,
+            authenticationInteractor = authenticationInteractor,
+            sceneInteractor = sceneInteractor,
+            deviceEntryFaceAuthRepository = deviceEntryFaceAuthRepository,
+            trustRepository = trustRepository,
+            flags = sceneContainerFlags,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
similarity index 89%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
index a780763..e6b7f62 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
@@ -18,4 +18,4 @@
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.featureFlagsClassic by Kosmos.Fixture { FakeFeatureFlagsClassic() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
index a780763..3d72967 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.keyguard.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository by
+    Kosmos.Fixture { fakeDeviceEntryFaceAuthRepository }
+val Kosmos.fakeDeviceEntryFaceAuthRepository by
+    Kosmos.Fixture { FakeDeviceEntryFaceAuthRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index df31a12..1381464 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -47,7 +47,7 @@
         get() = _isFaceAuthCurrentlyAllowed
 
     private val _isFaceAuthSupportedInCurrentPosture = MutableStateFlow(false)
-    override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
+    override val isFaceAuthSupportedInCurrentPosture: StateFlow<Boolean>
         get() = _isFaceAuthSupportedInCurrentPosture
 
     override val isCurrentUserInLockdown: Flow<Boolean>
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepositoryKosmos.kt
similarity index 78%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepositoryKosmos.kt
index a780763..b0e4ba0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.keyguard.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.inWindowLauncherUnlockAnimationRepository by
+    Kosmos.Fixture { InWindowLauncherUnlockAnimationRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryKosmos.kt
index a780763..453fef5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.keyguard.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.keyguardRepository: KeyguardRepository by Kosmos.Fixture { fakeKeyguardRepository }
+val Kosmos.fakeKeyguardRepository by Kosmos.Fixture { FakeKeyguardRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepositoryKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepositoryKosmos.kt
index a780763..c900ac9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepositoryKosmos.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.keyguard.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.keyguardSurfaceBehindRepository: KeyguardSurfaceBehindRepository by
+    Kosmos.Fixture { fakeKeyguardSurfaceBehindRepository }
+val Kosmos.fakeKeyguardSurfaceBehindRepository by
+    Kosmos.Fixture { FakeKeyguardSurfaceBehindRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
index a780763..008f79a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.keyguard.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.keyguardTransitionRepository: KeyguardTransitionRepository by
+    Kosmos.Fixture { fakeKeyguardTransitionRepository }
+val Kosmos.fakeKeyguardTransitionRepository by Kosmos.Fixture { FakeKeyguardTransitionRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/TrustRepositoryKosmos.kt
similarity index 75%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/TrustRepositoryKosmos.kt
index a780763..ca87acf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/TrustRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.keyguard.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.trustRepository: TrustRepository by Kosmos.Fixture { fakeTrustRepository }
+val Kosmos.fakeTrustRepository by Kosmos.Fixture { FakeTrustRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
new file mode 100644
index 0000000..b03d0b8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.flags.featureFlagsClassic
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.shade.data.repository.shadeRepository
+import dagger.Lazy
+
+val Kosmos.fromLockscreenTransitionInteractor by
+    Kosmos.Fixture {
+        FromLockscreenTransitionInteractor(
+            transitionRepository = keyguardTransitionRepository,
+            transitionInteractor = keyguardTransitionInteractor,
+            scope = applicationCoroutineScope,
+            keyguardInteractor = keyguardInteractor,
+            flags = featureFlagsClassic,
+            shadeRepository = shadeRepository,
+            powerInteractor = powerInteractor,
+            inWindowLauncherUnlockAnimationInteractor =
+                Lazy { inWindowLauncherUnlockAnimationInteractor },
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
new file mode 100644
index 0000000..ade3e1a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.keyguard.keyguardSecurityModel
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+
+val Kosmos.fromPrimaryBouncerTransitionInteractor by
+    Kosmos.Fixture {
+        FromPrimaryBouncerTransitionInteractor(
+            transitionRepository = keyguardTransitionRepository,
+            transitionInteractor = keyguardTransitionInteractor,
+            scope = applicationCoroutineScope,
+            keyguardInteractor = keyguardInteractor,
+            flags = featureFlagsClassic,
+            keyguardSecurityModel = keyguardSecurityModel,
+            selectedUserInteractor = selectedUserInteractor,
+            powerInteractor = powerInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt
new file mode 100644
index 0000000..dbbb203
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.data.repository.inWindowLauncherUnlockAnimationRepository
+import com.android.systemui.keyguard.data.repository.keyguardSurfaceBehindRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.shared.system.activityManagerWrapper
+import dagger.Lazy
+
+val Kosmos.inWindowLauncherUnlockAnimationInteractor by
+    Kosmos.Fixture {
+        InWindowLauncherUnlockAnimationInteractor(
+            repository = inWindowLauncherUnlockAnimationRepository,
+            scope = applicationCoroutineScope,
+            transitionInteractor = keyguardTransitionInteractor,
+            surfaceBehindRepository = Lazy { keyguardSurfaceBehindRepository },
+            activityManager = activityManagerWrapper,
+        )
+    }
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 6c2ce71..3d8ae1e 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
@@ -81,6 +81,7 @@
                 trustRepository,
                 testScope.backgroundScope,
                 mock(SelectedUserInteractor::class.java),
+                mock(KeyguardFaceAuthInteractor::class.java),
             )
         val alternateBouncerInteractor =
             AlternateBouncerInteractor(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
index d2ff9bc5..c575bb3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -40,7 +39,7 @@
     @JvmOverloads
     @JvmStatic
     fun create(
-        featureFlags: FakeFeatureFlags = createFakeFeatureFlags(),
+        featureFlags: FakeFeatureFlags = FakeFeatureFlags(),
         sceneContainerFlags: SceneContainerFlags = FakeSceneContainerFlags(),
         repository: FakeKeyguardRepository = FakeKeyguardRepository(),
         commandQueue: FakeCommandQueue = FakeCommandQueue(),
@@ -62,7 +61,6 @@
             KeyguardInteractor(
                 repository = repository,
                 commandQueue = commandQueue,
-                featureFlags = featureFlags,
                 sceneContainerFlags = sceneContainerFlags,
                 bouncerRepository = bouncerRepository,
                 configurationRepository = configurationRepository,
@@ -73,11 +71,6 @@
         )
     }
 
-    /** Provide defaults, otherwise tests will throw an error */
-    private fun createFakeFeatureFlags(): FakeFeatureFlags {
-        return FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
-    }
-
     data class WithDependencies(
         val repository: FakeKeyguardRepository,
         val commandQueue: FakeCommandQueue,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
new file mode 100644
index 0000000..bb84036
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.bouncer.data.repository.keyguardBouncerRepository
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.sceneContainerFlags
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.statusbar.commandQueue
+
+val Kosmos.keyguardInteractor by
+    Kosmos.Fixture {
+        KeyguardInteractor(
+            repository = keyguardRepository,
+            commandQueue = commandQueue,
+            powerInteractor = powerInteractor,
+            sceneContainerFlags = sceneContainerFlags,
+            bouncerRepository = keyguardBouncerRepository,
+            configurationRepository = configurationRepository,
+            shadeRepository = shadeRepository,
+            sceneInteractorProvider = { sceneInteractor },
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
new file mode 100644
index 0000000..e4d115e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import dagger.Lazy
+
+val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by
+    Kosmos.Fixture {
+        KeyguardTransitionInteractor(
+            scope = applicationCoroutineScope,
+            repository = keyguardTransitionRepository,
+            keyguardInteractor = Lazy { keyguardInteractor },
+            fromLockscreenTransitionInteractor = Lazy { fromLockscreenTransitionInteractor },
+            fromPrimaryBouncerTransitionInteractor =
+                Lazy { fromPrimaryBouncerTransitionInteractor },
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index b05915c..0b13858 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -1,16 +1,11 @@
 package com.android.systemui.kosmos
 
-import android.content.Context
-import android.os.UserManager
+import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.Kosmos.Fixture
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
-import org.mockito.Mockito
 
 var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
 var Kosmos.testScope by Fixture { TestScope(testDispatcher) }
-var Kosmos.context by Fixture<Context>()
-var Kosmos.lifecycleScope by Fixture<CoroutineScope>()
-
-val Kosmos.userManager by Fixture { Mockito.mock(UserManager::class.java) }
+var Kosmos.applicationCoroutineScope by Fixture { testScope.backgroundScope }
+var Kosmos.testCase: SysuiTestCase by Fixture()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
index a780763..0ec8d49 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.plugins
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.activityStarter by Kosmos.Fixture { mock<ActivityStarter>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
similarity index 77%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
index a780763..cac2646 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.plugins.statusbar
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.statusBarStateController by Kosmos.Fixture { mock<StatusBarStateController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/PowerRepositoryKosmos.kt
similarity index 75%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/PowerRepositoryKosmos.kt
index a780763..c924579 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/PowerRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.power.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.powerRepository: PowerRepository by Kosmos.Fixture { fakePowerRepository }
+val Kosmos.fakePowerRepository by Kosmos.Fixture { FakePowerRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt
new file mode 100644
index 0000000..8486691
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.power.domain.interactor
+
+import com.android.systemui.classifier.falsingCollector
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.power.data.repository.powerRepository
+import com.android.systemui.statusbar.phone.screenOffAnimationController
+
+val Kosmos.powerInteractor by
+    Kosmos.Fixture {
+        PowerInteractor(
+            repository = powerRepository,
+            falsingCollector = falsingCollector,
+            screenOffAnimationController = screenOffAnimationController,
+            statusBarStateController = statusBarStateController,
+        )
+    }
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 80c38b2..3c96051 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
@@ -120,7 +120,6 @@
     val testScope = kosmos.testScope
     val featureFlags =
         FakeFeatureFlagsClassic().apply {
-            set(Flags.FACE_AUTH_REFACTOR, false)
             set(Flags.FULL_SCREEN_USER_SWITCHER, false)
             set(Flags.NSSL_DEBUG_LINES, false)
         }
@@ -245,7 +244,6 @@
         return KeyguardInteractor(
             repository = repository,
             commandQueue = FakeCommandQueue(),
-            featureFlags = featureFlags,
             sceneContainerFlags = sceneContainerFlags,
             bouncerRepository = FakeKeyguardBouncerRepository(),
             configurationRepository = configurationRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt
index a780763..7c4e160 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.scene.data.repository
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.scene.shared.model.sceneContainerConfig
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.sceneContainerRepository by
+    Kosmos.Fixture { SceneContainerRepository(applicationCoroutineScope, sceneContainerConfig) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
new file mode 100644
index 0000000..9989876
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.logger.sceneLogger
+
+val Kosmos.sceneInteractor by
+    Kosmos.Fixture {
+        SceneInteractor(
+            applicationScope = applicationCoroutineScope,
+            repository = sceneContainerRepository,
+            powerInteractor = powerInteractor,
+            logger = sceneLogger,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt
similarity index 83%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt
index a780763..a3ceef0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.scene.shared.flag
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.sceneContainerFlags by Kosmos.Fixture { FakeSceneContainerFlags() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/logger/SceneLoggerKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/logger/SceneLoggerKosmos.kt
index a780763..c5f24f4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/logger/SceneLoggerKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.scene.shared.logger
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.sceneLogger by Kosmos.Fixture { mock<SceneLogger>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt
index a780763..f9cdc1b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.scene.shared.model
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.sceneContainerConfig by
+    Kosmos.Fixture { FakeSceneContainerConfigModule().sceneContainerConfig }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeRepositoryKosmos.kt
similarity index 75%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeRepositoryKosmos.kt
index a780763..38cedbc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.shade.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.shadeRepository: ShadeRepository by Kosmos.Fixture { fakeShadeRepository }
+val Kosmos.fakeShadeRepository by Kosmos.Fixture { FakeShadeRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
new file mode 100644
index 0000000..7da57f0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.shade.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.sceneContainerFlags
+import com.android.systemui.shade.ShadeModule
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
+import com.android.systemui.statusbar.phone.dozeParameters
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.userSetupRepository
+import com.android.systemui.statusbar.policy.data.repository.deviceProvisioningRepository
+import com.android.systemui.user.domain.interactor.userSwitcherInteractor
+
+var Kosmos.baseShadeInteractor: BaseShadeInteractor by
+    Kosmos.Fixture {
+        ShadeModule.provideBaseShadeInteractor(
+            sceneContainerFlags = sceneContainerFlags,
+            sceneContainerOn = { shadeInteractorSceneContainerImpl },
+            sceneContainerOff = { shadeInteractorLegacyImpl },
+        )
+    }
+val Kosmos.shadeInteractorSceneContainerImpl by
+    Kosmos.Fixture {
+        ShadeInteractorSceneContainerImpl(
+            scope = applicationCoroutineScope,
+            sceneInteractor = sceneInteractor,
+            sharedNotificationContainerInteractor = sharedNotificationContainerInteractor,
+        )
+    }
+val Kosmos.shadeInteractorLegacyImpl by
+    Kosmos.Fixture {
+        ShadeInteractorLegacyImpl(
+            scope = applicationCoroutineScope,
+            keyguardRepository = keyguardRepository,
+            sharedNotificationContainerInteractor = sharedNotificationContainerInteractor,
+            repository = shadeRepository
+        )
+    }
+var Kosmos.shadeInteractor: ShadeInteractor by Kosmos.Fixture { shadeInteractorImpl }
+val Kosmos.shadeInteractorImpl by
+    Kosmos.Fixture {
+        ShadeInteractorImpl(
+            scope = applicationCoroutineScope,
+            deviceProvisioningRepository = deviceProvisioningRepository,
+            disableFlagsRepository = disableFlagsRepository,
+            dozeParams = dozeParameters,
+            keyguardRepository = fakeKeyguardRepository,
+            keyguardTransitionInteractor = keyguardTransitionInteractor,
+            powerInteractor = powerInteractor,
+            userSetupRepository = userSetupRepository,
+            userSwitcherInteractor = userSwitcherInteractor,
+            baseShadeInteractor = baseShadeInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/ActivityManagerWrapperKosmos.kt
similarity index 78%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/ActivityManagerWrapperKosmos.kt
index a780763..e753593 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/ActivityManagerWrapperKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.shared.system
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.activityManagerWrapper by Kosmos.Fixture { mock<ActivityManagerWrapper>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt
index a780763..27f7f68 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.commandQueue by Kosmos.Fixture { mock<CommandQueue>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
index f25d282..6069083 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -16,14 +16,33 @@
 
 package com.android.systemui.statusbar.data.repository
 
+import android.view.Display
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.data.model.StatusBarAppearance
 import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.google.common.truth.Truth.assertThat
 import dagger.Binds
 import dagger.Module
 import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 
-class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepository {
+@SysUISingleton
+class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepositoryStore {
+
+    companion object {
+        const val DISPLAY_ID = Display.DEFAULT_DISPLAY
+    }
+
+    override val defaultDisplay: FakeStatusBarModePerDisplayRepository =
+        FakeStatusBarModePerDisplayRepository()
+
+    override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository {
+        assertThat(displayId).isEqualTo(DISPLAY_ID)
+        return defaultDisplay
+    }
+}
+
+class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository {
     override val isTransientShown = MutableStateFlow(false)
     override val isInFullscreenMode = MutableStateFlow(false)
     override val statusBarAppearance = MutableStateFlow<StatusBarAppearance?>(null)
@@ -39,5 +58,5 @@
 
 @Module
 interface FakeStatusBarModeRepositoryModule {
-    @Binds fun bindFake(fake: FakeStatusBarModeRepository): StatusBarModeRepository
+    @Binds fun bindFake(fake: FakeStatusBarModeRepository): StatusBarModeRepositoryStore
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepositoryKosmos.kt
similarity index 78%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepositoryKosmos.kt
index a780763..10151ac 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.notificationListenerSettingsRepository by
+    Kosmos.Fixture { NotificationListenerSettingsRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryKosmos.kt
similarity index 70%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryKosmos.kt
index a780763..a373a8e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.disableflags.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.disableFlagsRepository: DisableFlagsRepository by
+    Kosmos.Fixture { fakeDisableFlagsRepository }
+val Kosmos.fakeDisableFlagsRepository by Kosmos.Fixture { FakeDisableFlagsRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryKosmos.kt
similarity index 79%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryKosmos.kt
index a780763..5507d6c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryKosmos.kt
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.notification.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.activeNotificationListRepository by Kosmos.Fixture { ActiveNotificationListRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepositoryKosmos.kt
similarity index 76%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepositoryKosmos.kt
index a780763..ed62fda 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.notification.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.headsUpNotificationIconViewStateRepository by
+    Kosmos.Fixture { HeadsUpNotificationIconViewStateRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt
similarity index 64%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt
index a780763..f2b9da4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.notification.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.notificationsKeyguardViewStateRepository: NotificationsKeyguardViewStateRepository by
+    Kosmos.Fixture { fakeNotificationsKeyguardViewStateRepository }
+val Kosmos.fakeNotificationsKeyguardViewStateRepository by
+    Kosmos.Fixture { FakeNotificationsKeyguardViewStateRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
index a780763..3d7fb6d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.notification.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.activeNotificationsInteractor by
+    Kosmos.Fixture { ActiveNotificationsInteractor(activeNotificationListRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractorKosmos.kt
similarity index 66%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractorKosmos.kt
index a780763..d14c854 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractorKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.notification.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.data.repository.headsUpNotificationIconViewStateRepository
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.headsUpNotificationIconInteractor by
+    Kosmos.Fixture { HeadsUpNotificationIconInteractor(headsUpNotificationIconViewStateRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt
new file mode 100644
index 0000000..e7bd5ea
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.icon.domain.interactor
+
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.data.repository.notificationListenerSettingsRepository
+import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.wm.shell.bubbles.bubblesOptional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.alwaysOnDisplayNotificationIconsInteractor by
+    Kosmos.Fixture {
+        AlwaysOnDisplayNotificationIconsInteractor(
+            deviceEntryInteractor = deviceEntryInteractor,
+            iconsInteractor = notificationIconsInteractor,
+        )
+    }
+val Kosmos.statusBarNotificationIconsInteractor by
+    Kosmos.Fixture {
+        StatusBarNotificationIconsInteractor(
+            iconsInteractor = notificationIconsInteractor,
+            settingsRepository = notificationListenerSettingsRepository,
+        )
+    }
+val Kosmos.notificationIconsInteractor by
+    Kosmos.Fixture {
+        NotificationIconsInteractor(
+            activeNotificationsInteractor = activeNotificationsInteractor,
+            bubbles = bubblesOptional,
+            keyguardViewStateRepository = notificationsKeyguardViewStateRepository,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt
new file mode 100644
index 0000000..6295b83
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+
+import android.content.res.mainResources
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.icon.domain.interactor.alwaysOnDisplayNotificationIconsInteractor
+
+val Kosmos.notificationIconContainerAlwaysOnDisplayViewModel by
+    Kosmos.Fixture {
+        NotificationIconContainerAlwaysOnDisplayViewModel(
+            iconsInteractor = alwaysOnDisplayNotificationIconsInteractor,
+            keyguardInteractor = keyguardInteractor,
+            keyguardTransitionInteractor = keyguardTransitionInteractor,
+            resources = mainResources,
+            shadeInteractor = shadeInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt
new file mode 100644
index 0000000..04bb52d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+
+import android.content.res.mainResources
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor
+import com.android.systemui.statusbar.notification.icon.domain.interactor.statusBarNotificationIconsInteractor
+import com.android.systemui.statusbar.phone.domain.interactor.darkIconInteractor
+
+val Kosmos.notificationIconContainerStatusBarViewModel by
+    Kosmos.Fixture {
+        NotificationIconContainerStatusBarViewModel(
+            darkIconInteractor = darkIconInteractor,
+            iconsInteractor = statusBarNotificationIconsInteractor,
+            headsUpIconInteractor = headsUpNotificationIconInteractor,
+            keyguardInteractor = keyguardInteractor,
+            notificationsInteractor = activeNotificationsInteractor,
+            resources = mainResources,
+            shadeInteractor = shadeInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
new file mode 100644
index 0000000..3403227
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.domain.interactor
+
+import android.content.applicationContext
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.splitShadeStateController
+
+val Kosmos.sharedNotificationContainerInteractor by
+    Kosmos.Fixture {
+        SharedNotificationContainerInteractor(
+            configurationRepository = configurationRepository,
+            context = applicationContext,
+            splitShadeStateController = splitShadeStateController,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeParametersKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeParametersKosmos.kt
index a780763..9f6b181 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeParametersKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.phone
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.dozeParameters by Kosmos.Fixture { mock<DozeParameters>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ScreenOffAnimationControllerKosmos.kt
similarity index 77%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ScreenOffAnimationControllerKosmos.kt
index a780763..d4c21f6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ScreenOffAnimationControllerKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.phone
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.screenOffAnimationController by Kosmos.Fixture { mock<ScreenOffAnimationController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepositoryKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepositoryKosmos.kt
index a780763..977dcb7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.phone.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.darkIconRepository: DarkIconRepository by Kosmos.Fixture { fakeDarkIconRepository }
+val Kosmos.fakeDarkIconRepository by Kosmos.Fixture { FakeDarkIconRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractorKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractorKosmos.kt
index a780763..db678d4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractorKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.phone.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.data.repository.darkIconRepository
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.darkIconInteractor by Kosmos.Fixture { DarkIconInteractor(darkIconRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryKosmos.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryKosmos.kt
index a780763..7b9634a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.pipeline.mobile.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.userSetupRepository: UserSetupRepository by Kosmos.Fixture { fakeUserSetupRepository }
+val Kosmos.fakeUserSetupRepository by Kosmos.Fixture { FakeUserSetupRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt
similarity index 78%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt
index a780763..18a2f94 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.policy
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.configurationController by Kosmos.Fixture { mock<ConfigurationController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerKosmos.kt
similarity index 70%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerKosmos.kt
index a780763..6a77c88 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.policy
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.deviceProvisionedController: DeviceProvisionedController by
+    Kosmos.Fixture { fakeDeviceProvisionedController }
+val Kosmos.fakeDeviceProvisionedController by Kosmos.Fixture { FakeDeviceProvisionedController() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerKosmos.kt
index a780763..5e430381 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerKosmos.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.policy
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.splitShadeStateController: SplitShadeStateController by
+    Kosmos.Fixture { resourcesSplitShadeStateController }
+val Kosmos.resourcesSplitShadeStateController by
+    Kosmos.Fixture { ResourcesSplitShadeStateController() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryKosmos.kt
index a780763..56a0e02 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.statusbar.policy.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.deviceProvisioningRepository: DeviceProvisioningRepository by
+    Kosmos.Fixture { fakeDeviceProvisioningRepository }
+val Kosmos.fakeDeviceProvisioningRepository by Kosmos.Fixture { FakeDeviceProvisioningRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryKosmos.kt
index a780763..6bb5ec5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.telephony.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.telephonyRepository: TelephonyRepository by Kosmos.Fixture { fakeTelephonyRepository }
+val Kosmos.fakeTelephonyRepository by Kosmos.Fixture { FakeTelephonyRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractorKosmos.kt
similarity index 74%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractorKosmos.kt
index a780763..02ca96e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractorKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.telephony.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.telephony.data.repository.telephonyRepository
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.telephonyInteractor by Kosmos.Fixture { TelephonyInteractor(telephonyRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt
index 8bce9b6..9bb5262 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt
@@ -18,4 +18,5 @@
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.userRepository by Kosmos.Fixture { FakeUserRepository() }
+var Kosmos.userRepository: UserRepository by Kosmos.Fixture { fakeUserRepository }
+val Kosmos.fakeUserRepository by Kosmos.Fixture { FakeUserRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt
index e695704..3b1c3f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt
@@ -16,28 +16,32 @@
 
 package com.android.systemui.user.domain.interactor
 
+import android.app.admin.devicePolicyManager
+import android.content.applicationContext
+import android.os.userManager
+import com.android.internal.logging.uiEventLogger
+import com.android.systemui.guestResetOrExitSessionReceiver
+import com.android.systemui.guestResumeSessionReceiver
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.context
-import com.android.systemui.kosmos.lifecycleScope
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.userManager
+import com.android.systemui.statusbar.policy.deviceProvisionedController
 import com.android.systemui.user.data.repository.userRepository
-import com.android.systemui.util.mockito.mock
 
 val Kosmos.guestUserInteractor by
     Kosmos.Fixture {
         GuestUserInteractor(
-            applicationContext = context,
-            applicationScope = lifecycleScope,
+            applicationContext = applicationContext,
+            applicationScope = applicationCoroutineScope,
             mainDispatcher = testDispatcher,
             backgroundDispatcher = testDispatcher,
             manager = userManager,
-            deviceProvisionedController = mock(),
             repository = userRepository,
-            devicePolicyManager = mock(),
+            deviceProvisionedController = deviceProvisionedController,
+            devicePolicyManager = devicePolicyManager,
             refreshUsersScheduler = refreshUsersScheduler,
-            uiEventLogger = mock(),
-            resumeSessionReceiver = mock(),
-            resetOrExitSessionReceiver = mock(),
+            uiEventLogger = uiEventLogger,
+            resumeSessionReceiver = guestResumeSessionReceiver,
+            resetOrExitSessionReceiver = guestResetOrExitSessionReceiver,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeKosmos.kt
similarity index 82%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeKosmos.kt
index a780763..de9f69b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeKosmos.kt
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.user.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.headlessSystemUserMode by Kosmos.Fixture { HeadlessSystemUserModeImpl() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt
index 87a2fe0..14da8b0f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt
@@ -17,15 +17,11 @@
 package com.android.systemui.user.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.lifecycleScope
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.user.data.repository.userRepository
 
 val Kosmos.refreshUsersScheduler by
     Kosmos.Fixture {
-        RefreshUsersScheduler(
-            applicationScope = lifecycleScope,
-            mainDispatcher = testDispatcher,
-            repository = userRepository,
-        )
+        RefreshUsersScheduler(applicationCoroutineScope, testDispatcher, userRepository)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt
index a780763..427f92a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.user.domain.interactor
 
+import com.android.systemui.flags.featureFlagsClassic
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.user.data.repository.userRepository
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.selectedUserInteractor by
+    Kosmos.Fixture { SelectedUserInteractor(userRepository, featureFlagsClassic) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
index 6d6b268..42c77aa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
@@ -16,42 +16,41 @@
 
 package com.android.systemui.user.domain.interactor
 
+import android.app.activityManager
+import android.content.applicationContext
+import android.os.userManager
+import com.android.internal.logging.uiEventLogger
+import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.flags.featureFlags
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.context
-import com.android.systemui.kosmos.lifecycleScope
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.userManager
-import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
-import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.telephony.domain.interactor.telephonyInteractor
 import com.android.systemui.user.data.repository.userRepository
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.utils.userRestrictionChecker
 
 val Kosmos.userSwitcherInteractor by
     Kosmos.Fixture {
         UserSwitcherInteractor(
-            applicationContext = context,
+            applicationContext = applicationContext,
             repository = userRepository,
-            activityStarter = mock(),
-            keyguardInteractor =
-                KeyguardInteractorFactory.create(featureFlags = featureFlags).keyguardInteractor,
-            featureFlags = featureFlags,
+            activityStarter = activityStarter,
+            keyguardInteractor = keyguardInteractor,
+            featureFlags = featureFlagsClassic,
             manager = userManager,
-            headlessSystemUserMode = mock(),
-            applicationScope = lifecycleScope,
-            telephonyInteractor =
-                TelephonyInteractor(
-                    repository = FakeTelephonyRepository(),
-                ),
+            headlessSystemUserMode = headlessSystemUserMode,
+            applicationScope = applicationCoroutineScope,
+            telephonyInteractor = telephonyInteractor,
             broadcastDispatcher = broadcastDispatcher,
-            keyguardUpdateMonitor = mock(),
+            keyguardUpdateMonitor = keyguardUpdateMonitor,
             backgroundDispatcher = testDispatcher,
-            activityManager = mock(),
+            activityManager = activityManager,
             refreshUsersScheduler = refreshUsersScheduler,
             guestUserInteractor = guestUserInteractor,
-            uiEventLogger = mock(),
-            userRestrictionChecker = mock()
+            uiEventLogger = uiEventLogger,
+            userRestrictionChecker = userRestrictionChecker,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt
similarity index 85%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt
index a780763..914e654 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.util.time
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.fakeSystemClock by Kosmos.Fixture { FakeSystemClock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/UserRestrictionCheckerKosmos.kt
similarity index 84%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/utils/UserRestrictionCheckerKosmos.kt
index a780763..24d5d2f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/UserRestrictionCheckerKosmos.kt
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.systemui.utils
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.userRestrictionChecker by Kosmos.Fixture { UserRestrictionChecker() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/wm/shell/bubbles/BubblesKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/wm/shell/bubbles/BubblesKosmos.kt
index a780763..a7a37b2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/wm/shell/bubbles/BubblesKosmos.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.flags
+package com.android.wm.shell.bubbles
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+import java.util.Optional
 
-val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+var Kosmos.bubblesOptional by Kosmos.Fixture { Optional.of(bubbles) }
+var Kosmos.bubbles by Kosmos.Fixture { mock<Bubbles> {} }
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java
index 1d31579..f02f06c 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java
@@ -18,7 +18,6 @@
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -32,7 +31,7 @@
  *
  * @hide
  */
-@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+@Target({FIELD, METHOD, CONSTRUCTOR})
 @Retention(RetentionPolicy.CLASS)
 public @interface RavenwoodKeep {
 }
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepPartialClass.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepPartialClass.java
new file mode 100644
index 0000000..7847274
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepPartialClass.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ravenwood.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ *
+ * @hide
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodKeepPartialClass {
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepStaticInitializer.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepStaticInitializer.java
new file mode 100644
index 0000000..eeebee9
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepStaticInitializer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ravenwood.annotation;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * @hide
+ */
+@Target(TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodKeepStaticInitializer {
+}
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index 28639d4..48e9328 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -17,12 +17,14 @@
 class android.util.Log stubclass
 class android.util.Log !com.android.hoststubgen.nativesubstitution.Log_host
 class android.util.LogPrinter stubclass
+class android.util.LocalLog stubclass
 
 # String Manipulation
 class android.util.Printer stubclass
 class android.util.PrintStreamPrinter stubclass
 class android.util.PrintWriterPrinter stubclass
 class android.util.StringBuilderPrinter stubclass
+class android.util.IndentingPrintWriter stubclass
 
 # Properties
 class android.util.Property stubclass
@@ -76,6 +78,7 @@
 class android.util.UtilConfig stubclass
 
 # Internals
+class com.android.internal.util.FastPrintWriter stubclass
 class com.android.internal.util.GrowingArrayUtils stubclass
 class com.android.internal.util.LineBreakBufferedWriter stubclass
 class com.android.internal.util.Preconditions stubclass
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index df44fde..a791f682 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -2,6 +2,12 @@
 
 com.android.internal.util.ArrayUtils
 
+android.util.DataUnit
+android.util.EventLog
+android.util.IntArray
+android.util.LongArray
+android.util.Slog
+android.util.TimeUtils
 android.util.Xml
 
 android.os.Binder
diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/ravenwood-standard-options.txt
index 4b07ef6..f842f33 100644
--- a/ravenwood/ravenwood-standard-options.txt
+++ b/ravenwood/ravenwood-standard-options.txt
@@ -18,6 +18,9 @@
 --keep-annotation
     android.ravenwood.annotation.RavenwoodKeep
 
+--keep-annotation
+    android.ravenwood.annotation.RavenwoodKeepPartialClass
+
 --keep-class-annotation
     android.ravenwood.annotation.RavenwoodKeepWholeClass
 
@@ -35,3 +38,6 @@
 
 --class-load-hook-annotation
     android.ravenwood.annotation.RavenwoodClassLoadHook
+
+--keep-static-initializer-annotation
+    android.ravenwood.annotation.RavenwoodKeepStaticInitializer
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 5635dd5..42ab05f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -21,6 +21,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityOptions;
 import android.app.Dialog;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -205,7 +206,10 @@
                         intent,
                         PendingIntent.FLAG_MUTABLE
                                 | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT,
-                        /* options= */ null, UserHandle.CURRENT);
+                        ActivityOptions.makeBasic()
+                                .setPendingIntentCreatorBackgroundActivityStartMode(
+                                        ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+                                .toBundle(), UserHandle.CURRENT);
                 if (sDebug) {
                     Slog.d(TAG, "startActivity add save UI restored with intent=" + intent);
                 }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b9c269c..71a1f01 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -75,7 +75,6 @@
 import android.companion.IOnTransportsChangedListener;
 import android.companion.ISystemDataTransferCallback;
 import android.companion.datatransfer.PermissionSyncRequest;
-import android.companion.utils.FeatureUtils;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.SharedPreferences;
@@ -829,11 +828,6 @@
         @Override
         public PendingIntent buildPermissionTransferUserConsentIntent(String packageName,
                 int userId, int associationId) {
-            if (!FeatureUtils.isPermSyncEnabled()) {
-                throw new UnsupportedOperationException("Calling"
-                        + " buildPermissionTransferUserConsentIntent, but this API is disabled by"
-                        + " the system.");
-            }
             return mSystemDataTransferProcessor.buildPermissionTransferUserConsentIntent(
                     packageName, userId, associationId);
         }
@@ -841,10 +835,6 @@
         @Override
         public void startSystemDataTransfer(String packageName, int userId, int associationId,
                 ISystemDataTransferCallback callback) {
-            if (!FeatureUtils.isPermSyncEnabled()) {
-                throw new UnsupportedOperationException("Calling startSystemDataTransfer, but this"
-                        + " API is disabled by the system.");
-            }
             mSystemDataTransferProcessor.startSystemDataTransfer(packageName, userId,
                     associationId, callback);
         }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 1f62613..23e7ce6 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -20,6 +20,7 @@
 
 import android.companion.AssociationInfo;
 import android.companion.ContextSyncMessage;
+import android.companion.Flags;
 import android.companion.Telecom;
 import android.companion.datatransfer.PermissionSyncRequest;
 import android.net.MacAddress;
@@ -65,7 +66,14 @@
     public int onCommand(String cmd) {
         final PrintWriter out = getOutPrintWriter();
         final int associationId;
+
         try {
+            if ("simulate-device-event".equals(cmd) && Flags.devicePresence()) {
+                associationId = getNextIntArgRequired();
+                int event = getNextIntArgRequired();
+                mDevicePresenceMonitor.simulateDeviceEvent(associationId, event);
+                return 0;
+            }
             switch (cmd) {
                 case "list": {
                     final int userId = getNextIntArgRequired();
@@ -107,10 +115,15 @@
                     mService.loadAssociationsFromDisk();
                     break;
 
-                case "simulate-device-event":
+                case "simulate-device-appeared":
                     associationId = getNextIntArgRequired();
-                    int event = getNextIntArgRequired();
-                    mDevicePresenceMonitor.simulateDeviceEvent(associationId, event);
+                    mDevicePresenceMonitor.simulateDeviceEvent(associationId, /* event */ 0);
+                    break;
+
+                case "simulate-device-disappeared":
+                    associationId = getNextIntArgRequired();
+                    mDevicePresenceMonitor.simulateDeviceEvent(associationId, /* event */ 1);
+                    break;
 
                 case "remove-inactive-associations": {
                     // This command should trigger the same "clean-up" job as performed by the
@@ -346,9 +359,7 @@
         pw.println("      information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
         pw.println("      USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
 
-        pw.println("  simulate-device-event ASSOCIATION_ID EVENT");
-        pw.println("  Simulate the companion device event changes:");
-        pw.println("    Case(0): ");
+        pw.println("  simulate-device-appeared ASSOCIATION_ID");
         pw.println("      Make CDM act as if the given companion device has appeared.");
         pw.println("      I.e. bind the associated companion application's");
         pw.println("      CompanionDeviceService(s) and trigger onDeviceAppeared() callback.");
@@ -356,18 +367,43 @@
         pw.println("      will act as if device disappeared, unless 'simulate-device-disappeared'");
         pw.println("      or 'simulate-device-appeared' is called again before 60 seconds run out"
                 + ".");
-        pw.println("    Case(1): ");
+        pw.println("      USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
+        pw.println("  simulate-device-disappeared ASSOCIATION_ID");
         pw.println("      Make CDM act as if the given companion device has disappeared.");
         pw.println("      I.e. unbind the associated companion application's");
         pw.println("      CompanionDeviceService(s) and trigger onDeviceDisappeared() callback.");
         pw.println("      NOTE: This will only have effect if 'simulate-device-appeared' was");
         pw.println("      invoked for the same device (same ASSOCIATION_ID) no longer than");
         pw.println("      60 seconds ago.");
-        pw.println("    Case(2): ");
-        pw.println("      Make CDM act as if the given companion device is BT connected ");
-        pw.println("    Case(3): ");
-        pw.println("      Make CDM act as if the given companion device is BT disconnected ");
-        pw.println("      USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
+        if (Flags.devicePresence()) {
+            pw.println("  simulate-device-event ASSOCIATION_ID EVENT");
+            pw.println("  Simulate the companion device event changes:");
+            pw.println("    Case(0): ");
+            pw.println("      Make CDM act as if the given companion device has appeared.");
+            pw.println("      I.e. bind the associated companion application's");
+            pw.println("      CompanionDeviceService(s) and trigger onDeviceAppeared() callback.");
+            pw.println("      The CDM will consider the devices as present for"
+                    + "60 seconds and then");
+            pw.println("      will act as if device disappeared, unless"
+                    + "'simulate-device-disappeared'");
+            pw.println("      or 'simulate-device-appeared' is called again before 60 seconds"
+                    + "run out.");
+            pw.println("    Case(1): ");
+            pw.println("      Make CDM act as if the given companion device has disappeared.");
+            pw.println("      I.e. unbind the associated companion application's");
+            pw.println("      CompanionDeviceService(s) and trigger onDeviceDisappeared()"
+                    + "callback.");
+            pw.println("      NOTE: This will only have effect if 'simulate-device-appeared' was");
+            pw.println("      invoked for the same device (same ASSOCIATION_ID) no longer than");
+            pw.println("      60 seconds ago.");
+            pw.println("    Case(2): ");
+            pw.println("      Make CDM act as if the given companion device is BT connected ");
+            pw.println("    Case(3): ");
+            pw.println("      Make CDM act as if the given companion device is BT disconnected ");
+            pw.println("      USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+        }
 
         pw.println("  remove-inactive-associations");
         pw.println("      Remove self-managed associations that have not been active ");
diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
index 8fea078..e42b935 100644
--- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -79,7 +79,7 @@
         void onDeviceDisappeared(int associationId);
 
         /**Invoked when device has corresponding event changes. */
-        void onDeviceEvent(int associationId, int state);
+        void onDeviceEvent(int associationId, int event);
     }
 
     private final @NonNull AssociationStore mAssociationStore;
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 215970e..e51ef29 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -864,6 +864,23 @@
         }
 
         @Override
+        public @NonNull Set<String> getAllPersistentDeviceIds() {
+            Set<String> persistentIds = new ArraySet<>();
+            synchronized (mVirtualDeviceManagerLock) {
+                for (int i = 0; i < mActiveAssociations.size(); ++i) {
+                    AssociationInfo associationInfo = mActiveAssociations.get(i);
+                    if (VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(
+                            associationInfo.getDeviceProfile())) {
+                        persistentIds.add(
+                                VirtualDeviceImpl.createPersistentDeviceId(
+                                        associationInfo.getId()));
+                    }
+                }
+            }
+            return persistentIds;
+        }
+
+        @Override
         public void registerAppsOnVirtualDeviceListener(
                 @NonNull AppsOnVirtualDeviceListener listener) {
             synchronized (mVirtualDeviceManagerLock) {
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
index a570d09..6940ffe 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
@@ -69,6 +69,11 @@
             }
 
             @Override
+            public void onProcessCaptureRequest(int streamId, int frameId) throws RemoteException {
+                camera.onProcessCaptureRequest(streamId, frameId, /*metadata=*/ null);
+            }
+
+            @Override
             public void onStreamClosed(int streamId) throws RemoteException {
                 camera.onStreamClosed(streamId);
             }
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 7907d61..77b6d583 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -1182,8 +1182,8 @@
 
         // we are only interested in doing things at PHASE_BOOT_COMPLETED
         if (phase == PHASE_BOOT_COMPLETED) {
-            Slog.i(TAG, "Boot completed. Getting VBMeta Digest.");
-            getVBMetaDigestInformation();
+            Slog.i(TAG, "Boot completed. Getting boot integrity data.");
+            collectBootIntegrityInfo();
 
             // Log to statsd
             // TODO(b/264061957): For now, biometric system properties are always collected if users
@@ -1458,10 +1458,19 @@
         }
     }
 
-    private void getVBMetaDigestInformation() {
+    private void collectBootIntegrityInfo() {
         mVbmetaDigest = SystemProperties.get(SYSPROP_NAME_VBETA_DIGEST, VBMETA_DIGEST_UNAVAILABLE);
         Slog.d(TAG, String.format("VBMeta Digest: %s", mVbmetaDigest));
         FrameworkStatsLog.write(FrameworkStatsLog.VBMETA_DIGEST_REPORTED, mVbmetaDigest);
+
+        if (android.security.Flags.binaryTransparencySepolicyHash()) {
+            byte[] sepolicyHash = PackageUtils.computeSha256DigestForLargeFileAsBytes(
+                    "/sys/fs/selinux/policy", PackageUtils.createLargeFileBuffer());
+            String sepolicyHashEncoded = HexEncoding.encodeToString(sepolicyHash, false);
+            Slog.d(TAG, "sepolicy hash: " + sepolicyHashEncoded);
+            FrameworkStatsLog.write(FrameworkStatsLog.BOOT_INTEGRITY_INFO_REPORTED,
+                    sepolicyHashEncoded, mVbmetaDigest);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 8cd5ce1..ce1a875 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -158,6 +158,10 @@
     @GuardedBy("this")
     private ArraySet<Integer> mPinKeys;
 
+    // Note that we don't use the `_BOOT` namespace for anonymous pinnings, as we want
+    // them to be responsive to dynamic flag changes for experimentation.
+    private static final String DEVICE_CONFIG_NAMESPACE_ANON_SIZE =
+            DeviceConfig.NAMESPACE_RUNTIME_NATIVE;
     private static final String DEVICE_CONFIG_KEY_ANON_SIZE = "pin_shared_anon_size";
     private static final long DEFAULT_ANON_SIZE =
             SystemProperties.getLong("pinner.pin_shared_anon_size", 0);
@@ -188,11 +192,11 @@
         }
     };
 
-    private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener =
+    private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigAnonSizeListener =
             new DeviceConfig.OnPropertiesChangedListener() {
                 @Override
                 public void onPropertiesChanged(DeviceConfig.Properties properties) {
-                    if (DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT.equals(properties.getNamespace())
+                    if (DEVICE_CONFIG_NAMESPACE_ANON_SIZE.equals(properties.getNamespace())
                             && properties.getKeyset().contains(DEVICE_CONFIG_KEY_ANON_SIZE)) {
                         refreshPinAnonConfig();
                     }
@@ -246,9 +250,9 @@
         registerUserSetupCompleteListener();
 
         mDeviceConfigInterface.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+                DEVICE_CONFIG_NAMESPACE_ANON_SIZE,
                 new HandlerExecutor(mPinnerHandler),
-                mDeviceConfigListener);
+                mDeviceConfigAnonSizeListener);
     }
 
     @Override
@@ -733,7 +737,7 @@
     private void refreshPinAnonConfig() {
         long newPinAnonSize =
                 mDeviceConfigInterface.getLong(
-                        DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+                        DEVICE_CONFIG_NAMESPACE_ANON_SIZE,
                         DEVICE_CONFIG_KEY_ANON_SIZE,
                         DEFAULT_ANON_SIZE);
         newPinAnonSize = Math.max(0, Math.min(newPinAnonSize, MAX_ANON_SIZE));
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 5b54561..e07631c 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -193,6 +193,12 @@
     private @Nullable BroadcastProcessQueue mRunningColdStart;
 
     /**
+     * Indicates whether we have queued a message to check pending cold start validity.
+     */
+    @GuardedBy("mService")
+    private boolean mCheckPendingColdStartQueued;
+
+    /**
      * Collection of latches waiting for device to reach specific state. The
      * first argument is a function to test for the desired state, and the
      * second argument is the latch to release once that state is reached.
@@ -302,7 +308,11 @@
                 return true;
             }
             case MSG_CHECK_PENDING_COLD_START_VALIDITY: {
-                checkPendingColdStartValidity();
+                synchronized (mService) {
+                    /* Clear this as we have just received the broadcast. */
+                    mCheckPendingColdStartQueued = false;
+                    checkPendingColdStartValidityLocked();
+                }
                 return true;
             }
             case MSG_PROCESS_FREEZABLE_CHANGED: {
@@ -549,7 +559,7 @@
             mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);
         }
 
-        checkPendingColdStartValidity();
+        checkPendingColdStartValidityLocked();
         checkAndRemoveWaitingFor();
 
         traceEnd(cookie);
@@ -573,22 +583,24 @@
         enqueueUpdateRunningList();
     }
 
-    private void checkPendingColdStartValidity() {
+    @GuardedBy("mService")
+    private void checkPendingColdStartValidityLocked() {
         // There are a few cases where a starting process gets killed but AMS doesn't report
         // this event. So, once we start waiting for a pending cold start, periodically check
         // if the pending start is still valid and if not, clear it so that the queue doesn't
         // keep waiting for the process start forever.
-        synchronized (mService) {
-            // If there is no pending cold start, then nothing to do.
-            if (mRunningColdStart == null) {
-                return;
-            }
-            if (isPendingColdStartValid()) {
+        // If there is no pending cold start, then nothing to do.
+        if (mRunningColdStart == null) {
+            return;
+        }
+        if (isPendingColdStartValid()) {
+            if (!mCheckPendingColdStartQueued) {
                 mLocalHandler.sendEmptyMessageDelayed(MSG_CHECK_PENDING_COLD_START_VALIDITY,
                         mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS);
-            } else {
-                clearInvalidPendingColdStart();
+                mCheckPendingColdStartQueued = true;
             }
+        } else {
+            clearInvalidPendingColdStart();
         }
     }
 
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index b852ef5..d372108 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -67,10 +67,8 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal.OomAdjReason;
 import android.content.pm.ServiceInfo;
-import android.os.IBinder;
 import android.os.SystemClock;
 import android.os.Trace;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 
@@ -80,7 +78,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.function.Consumer;
@@ -504,6 +501,28 @@
         }
     }
 
+    /**
+     * A helper consumer for collecting processes that have not been reached yet. To avoid object
+     * allocations every OomAdjuster update, the results will be stored in
+     * {@link UnreachedProcessCollector#processList}. The process list reader is responsible
+     * for setting it before usage, as well as, clearing the reachable state of each process in the
+     * list.
+     */
+    private static class UnreachedProcessCollector implements Consumer<ProcessRecord> {
+        public ArrayList<ProcessRecord> processList = null;
+        @Override
+        public void accept(ProcessRecord process) {
+            if (process.mState.isReachable()) {
+                return;
+            }
+            process.mState.setReachable(true);
+            processList.add(process);
+        }
+    }
+
+    private final UnreachedProcessCollector mUnreachedProcessCollector =
+            new UnreachedProcessCollector();
+
     OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
             ActiveUids activeUids) {
         this(service, processList, activeUids, createAdjusterThread());
@@ -755,23 +774,8 @@
         // We'll need to collect the upstream processes of the target apps here, because those
         // processes would potentially impact the procstate/adj via bindings.
         if (!fullUpdate) {
-            final boolean containsCycle = collectReversedReachableProcessesLocked(targetProcesses,
-                    clientProcesses);
+            collectExcludedClientProcessesLocked(targetProcesses, clientProcesses);
 
-            // If any of its upstream processes are in a cycle,
-            // move them into the candidate targets.
-            if (containsCycle) {
-                // Add all client apps to the target process list.
-                for (int i = 0, size = clientProcesses.size(); i < size; i++) {
-                    final ProcessRecord client = clientProcesses.get(i);
-                    final UidRecord uidRec = client.getUidRecord();
-                    targetProcesses.add(client);
-                    if (uidRec != null) {
-                        uids.put(uidRec.getUid(), uidRec);
-                    }
-                }
-                clientProcesses.clear();
-            }
             for (int i = 0, size = targetProcesses.size(); i < size; i++) {
                 final ProcessRecord app = targetProcesses.valueAt(i);
                 app.mState.resetCachedInfo();
@@ -807,102 +811,36 @@
     }
 
     /**
-     * Collect the reversed reachable processes from the given {@code apps}, the result will be
-     * returned in the given {@code processes}, which will <em>NOT</em> include the processes from
-     * the given {@code apps}.
+     * Collect the client processes from the given {@code apps}, the result will be returned in the
+     * given {@code clientProcesses}, which will <em>NOT</em> include the processes from the given
+     * {@code apps}.
      */
     @GuardedBy("mService")
-    private boolean collectReversedReachableProcessesLocked(ArraySet<ProcessRecord> apps,
+    private void collectExcludedClientProcessesLocked(ArraySet<ProcessRecord> apps,
             ArrayList<ProcessRecord> clientProcesses) {
-        final ArrayDeque<ProcessRecord> queue = mTmpQueue;
-        queue.clear();
-        clientProcesses.clear();
-        for (int i = 0, size = apps.size(); i < size; i++) {
+        // Mark all of the provided apps as reachable to avoid including them in the client list.
+        final int appsSize = apps.size();
+        for (int i = 0; i < appsSize; i++) {
             final ProcessRecord app = apps.valueAt(i);
             app.mState.setReachable(true);
-            app.mState.setReversedReachable(true);
-            queue.offer(app);
         }
 
-        // Track if any of them reachables could include a cycle
-        boolean containsCycle = false;
-
-        // Scan upstreams of the process record
-        for (ProcessRecord pr = queue.poll(); pr != null; pr = queue.poll()) {
-            if (!pr.mState.isReachable()) {
-                // If not in the given initial set of apps, add it.
-                clientProcesses.add(pr);
-            }
-            final ProcessServiceRecord psr = pr.mServices;
-            for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
-                final ServiceRecord s = psr.getRunningServiceAt(i);
-                final ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
-                        s.getConnections();
-                for (int j = serviceConnections.size() - 1; j >= 0; j--) {
-                    final ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(j);
-                    for (int k = clist.size() - 1; k >= 0; k--) {
-                        final ConnectionRecord cr = clist.get(k);
-                        final ProcessRecord client = cr.binding.client;
-                        containsCycle |= client.mState.isReversedReachable();
-                        if (client.mState.isReversedReachable()) {
-                            continue;
-                        }
-                        queue.offer(client);
-                        client.mState.setReversedReachable(true);
-                    }
-                }
-            }
-            final ProcessProviderRecord ppr = pr.mProviders;
-            for (int i = ppr.numberOfProviders() - 1; i >= 0; i--) {
-                final ContentProviderRecord cpr = ppr.getProviderAt(i);
-                for (int j = cpr.connections.size() - 1; j >= 0; j--) {
-                    final ContentProviderConnection conn = cpr.connections.get(j);
-                    final ProcessRecord client = conn.client;
-                    containsCycle |= client.mState.isReversedReachable();
-                    if (client.mState.isReversedReachable()) {
-                        continue;
-                    }
-                    queue.offer(client);
-                    client.mState.setReversedReachable(true);
-                }
-            }
-            // If this process is a sandbox itself, also add the app on whose behalf
-            // its running
-            if (pr.isSdkSandbox) {
-                for (int is = psr.numberOfRunningServices() - 1; is >= 0; is--) {
-                    ServiceRecord s = psr.getRunningServiceAt(is);
-                    ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
-                            s.getConnections();
-                    for (int conni = serviceConnections.size() - 1; conni >= 0; conni--) {
-                        ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni);
-                        for (int i = clist.size() - 1; i >= 0; i--) {
-                            ConnectionRecord cr = clist.get(i);
-                            ProcessRecord attributedApp = cr.binding.attributedClient;
-                            if (attributedApp == null || attributedApp == pr) {
-                                continue;
-                            }
-                            containsCycle |= attributedApp.mState.isReversedReachable();
-                            if (attributedApp.mState.isReversedReachable()) {
-                                continue;
-                            }
-                            queue.offer(attributedApp);
-                            attributedApp.mState.setReversedReachable(true);
-                        }
-                    }
-                }
-            }
+        clientProcesses.clear();
+        mUnreachedProcessCollector.processList = clientProcesses;
+        for (int i = 0; i < appsSize; i++) {
+            final ProcessRecord app = apps.valueAt(i);
+            app.forEachClient(mUnreachedProcessCollector);
         }
+        mUnreachedProcessCollector.processList = null;
 
         // Reset the temporary bits.
         for (int i = clientProcesses.size() - 1; i >= 0; i--) {
-            clientProcesses.get(i).mState.setReversedReachable(false);
+            clientProcesses.get(i).mState.setReachable(false);
         }
         for (int i = 0, size = apps.size(); i < size; i++) {
             final ProcessRecord app = apps.valueAt(i);
             app.mState.setReachable(false);
-            app.mState.setReversedReachable(false);
         }
-        return containsCycle;
     }
 
     @GuardedBy({"mService", "mProcLock"})
@@ -917,10 +855,6 @@
         final int procStateTarget = mProcessRecordProcStateNodes.size() - 1;
         final int adjTarget = mProcessRecordAdjNodes.size() - 1;
 
-        final int appUid = !fullUpdate && targetProcesses.size() > 0
-                ? targetProcesses.valueAt(0).uid : -1;
-        final int logUid = mService.mCurOomAdjUid;
-
         mAdjSeq++;
         // All apps to be updated will be moved to the lowest slot.
         if (fullUpdate) {
@@ -974,7 +908,7 @@
             // We don't update the adj list since we're resetting it below.
         }
 
-        // Now nodes are set into their slots, without facting in the bindings.
+        // Now nodes are set into their slots, without factoring in the bindings.
         // The nodes between the `lastNode` pointer and the TAIL should be the new nodes.
         //
         // The whole rationale here is that, the bindings from client to host app, won't elevate
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 2c6e598..b2082d9 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -69,8 +69,10 @@
 import com.android.server.wm.WindowProcessListener;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * Full information about a particular process that
@@ -1613,4 +1615,50 @@
     public boolean wasForceStopped() {
         return mWasForceStopped;
     }
+
+    /**
+     * Traverses all client processes and feed them to consumer.
+     */
+    @GuardedBy("mProcLock")
+    void forEachClient(@NonNull Consumer<ProcessRecord> consumer) {
+        for (int i = mServices.numberOfRunningServices() - 1; i >= 0; i--) {
+            final ServiceRecord s = mServices.getRunningServiceAt(i);
+            final ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
+                    s.getConnections();
+            for (int j = serviceConnections.size() - 1; j >= 0; j--) {
+                final ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(j);
+                for (int k = clist.size() - 1; k >= 0; k--) {
+                    final ConnectionRecord cr = clist.get(k);
+                    consumer.accept(cr.binding.client);
+                }
+            }
+        }
+        for (int i = mProviders.numberOfProviders() - 1; i >= 0; i--) {
+            final ContentProviderRecord cpr = mProviders.getProviderAt(i);
+            for (int j = cpr.connections.size() - 1; j >= 0; j--) {
+                final ContentProviderConnection conn = cpr.connections.get(j);
+                consumer.accept(conn.client);
+            }
+        }
+        // If this process is a sandbox itself, also add the app on whose behalf
+        // its running
+        if (isSdkSandbox) {
+            for (int is = mServices.numberOfRunningServices() - 1; is >= 0; is--) {
+                ServiceRecord s = mServices.getRunningServiceAt(is);
+                ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
+                        s.getConnections();
+                for (int conni = serviceConnections.size() - 1; conni >= 0; conni--) {
+                    ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni);
+                    for (int i = clist.size() - 1; i >= 0; i--) {
+                        ConnectionRecord cr = clist.get(i);
+                        ProcessRecord attributedApp = cr.binding.attributedClient;
+                        if (attributedApp == null || attributedApp == this) {
+                            continue;
+                        }
+                        consumer.accept(attributedApp);
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 8723c5d..5ad921f 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -379,12 +379,6 @@
     private boolean mReachable;
 
     /**
-     * Whether or not this process is reversed reachable from given process.
-     */
-    @GuardedBy("mService")
-    private boolean mReversedReachable;
-
-    /**
      * The most recent time when the last visible activity within this process became invisible.
      *
      * <p> It'll be set to 0 if there is never a visible activity, or Long.MAX_VALUE if there is
@@ -997,16 +991,6 @@
     }
 
     @GuardedBy("mService")
-    boolean isReversedReachable() {
-        return mReversedReachable;
-    }
-
-    @GuardedBy("mService")
-    void setReversedReachable(boolean reversedReachable) {
-        mReversedReachable = reversedReachable;
-    }
-
-    @GuardedBy("mService")
     void resetCachedInfo() {
         mCachedHasActivities = VALUE_INVALID;
         mCachedIsHeavyWeight = VALUE_INVALID;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1ef4333..df106a7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -154,7 +154,6 @@
 import android.media.projection.IMediaProjection;
 import android.media.projection.IMediaProjectionCallback;
 import android.media.projection.IMediaProjectionManager;
-import android.media.session.MediaSessionManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -312,9 +311,6 @@
     private final ContentResolver mContentResolver;
     private final AppOpsManager mAppOps;
 
-    /** do not use directly, use getMediaSessionManager() which handles lazy initialization */
-    @Nullable private volatile MediaSessionManager mMediaSessionManager;
-
     // the platform type affects volume and silent mode behavior
     private final int mPlatformType;
 
@@ -945,8 +941,6 @@
 
     private final SoundDoseHelper mSoundDoseHelper;
 
-    private final HardeningEnforcer mHardeningEnforcer;
-
     private final Object mSupportedSystemUsagesLock = new Object();
     @GuardedBy("mSupportedSystemUsagesLock")
     private @AttributeSystemUsage int[] mSupportedSystemUsages =
@@ -1321,8 +1315,6 @@
         mDisplayManager = context.getSystemService(DisplayManager.class);
 
         mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler);
-
-        mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive());
     }
 
     private void initVolumeStreamStates() {
@@ -1394,6 +1386,7 @@
 
         // check on volume initialization
         checkVolumeRangeInitialization("AudioService()");
+
     }
 
     private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener =
@@ -1406,14 +1399,6 @@
                 }
             };
 
-    private MediaSessionManager getMediaSessionManager() {
-        if (mMediaSessionManager == null) {
-            mMediaSessionManager = (MediaSessionManager) mContext
-                    .getSystemService(Context.MEDIA_SESSION_SERVICE);
-        }
-        return mMediaSessionManager;
-    }
-
     /**
      * Initialize intent receives and settings observers for this service.
      * Must be called after createStreamStates() as the handling of some events
@@ -3442,10 +3427,6 @@
      * Part of service interface, check permissions here */
     public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags,
             String callingPackage, String attributionTag) {
-        if (mHardeningEnforcer.blockVolumeMethod(
-                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME)) {
-            return;
-        }
         if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
             Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without"
                     + "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
@@ -4222,10 +4203,6 @@
      * Part of service interface, check permissions here */
     public void setStreamVolumeWithAttribution(int streamType, int index, int flags,
             String callingPackage, String attributionTag) {
-        if (mHardeningEnforcer.blockVolumeMethod(
-                HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME)) {
-            return;
-        }
         setStreamVolumeWithAttributionInt(streamType, index, flags, /*device*/ null,
                 callingPackage, attributionTag);
     }
@@ -5080,7 +5057,6 @@
     /** @see AudioManager#setMasterMute(boolean, int) */
     public void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
             String attributionTag) {
-
         super.setMasterMute_enforcePermission();
 
         setMasterMuteInternal(mute, flags, callingPackage,
@@ -5446,10 +5422,6 @@
     }
 
     public void setRingerModeExternal(int ringerMode, String caller) {
-        if (mHardeningEnforcer.blockVolumeMethod(
-                HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_RINGER_MODE)) {
-            return;
-        }
         if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode)
                 && !mNm.isNotificationPolicyAccessGrantedForPackage(caller)) {
             throw new SecurityException("Not allowed to change Do Not Disturb state");
@@ -6202,35 +6174,6 @@
                 AudioDeviceVolumeManager.ADJUST_MODE_NORMAL);
     }
 
-    /**
-      * @see AudioManager#adjustVolume(int, int)
-      * This method is redirected from AudioManager to AudioService for API hardening rules
-      * enforcement then to MediaSession for implementation.
-      */
-    @Override
-    public void adjustVolume(int direction, int flags) {
-        if (mHardeningEnforcer.blockVolumeMethod(
-                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_VOLUME)) {
-            return;
-        }
-        getMediaSessionManager().dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
-                    direction, flags);
-    }
-
-    /**
-     * @see AudioManager#adjustSuggestedStreamVolume(int, int, int)
-     * This method is redirected from AudioManager to AudioService for API hardening rules
-     * enforcement then to MediaSession for implementation.
-     */
-    @Override
-    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
-        if (mHardeningEnforcer.blockVolumeMethod(
-                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME)) {
-            return;
-        }
-        getMediaSessionManager().dispatchAdjustVolume(suggestedStreamType, direction, flags);
-    }
-
     /** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
     @Override
     public void setStreamVolumeForUid(int streamType, int index, int flags,
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
deleted file mode 100644
index 4ceb83b2..0000000
--- a/services/core/java/com/android/server/audio/HardeningEnforcer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.audio;
-
-import static android.media.audio.Flags.autoPublicVolumeApiHardening;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.media.AudioManager;
-import android.os.Binder;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Log;
-
-/**
- * Class to encapsulate all audio API hardening operations
- */
-public class HardeningEnforcer {
-
-    private static final String TAG = "AS.HardeningEnforcer";
-
-    final Context mContext;
-    final boolean mIsAutomotive;
-
-    /**
-     * Matches calls from {@link AudioManager#setStreamVolume(int, int, int)}
-     */
-    public static final int METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME = 100;
-    /**
-     * Matches calls from {@link AudioManager#adjustVolume(int, int)}
-     */
-    public static final int METHOD_AUDIO_MANAGER_ADJUST_VOLUME = 101;
-    /**
-     * Matches calls from {@link AudioManager#adjustSuggestedStreamVolume(int, int, int)}
-     */
-    public static final int METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME = 102;
-    /**
-     * Matches calls from {@link AudioManager#adjustStreamVolume(int, int, int)}
-     */
-    public static final int METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME = 103;
-    /**
-     * Matches calls from {@link AudioManager#setRingerMode(int)}
-     */
-    public static final int METHOD_AUDIO_MANAGER_SET_RINGER_MODE = 200;
-
-    public HardeningEnforcer(Context ctxt, boolean isAutomotive) {
-        mContext = ctxt;
-        mIsAutomotive = isAutomotive;
-    }
-
-    /**
-     * Checks whether the call in the current thread should be allowed or blocked
-     * @param volumeMethod name of the method to check, for logging purposes
-     * @return false if the method call is allowed, true if it should be a no-op
-     */
-    protected boolean blockVolumeMethod(int volumeMethod) {
-        // for Auto, volume methods require MODIFY_AUDIO_SETTINGS_PRIVILEGED
-        if (mIsAutomotive) {
-            if (!autoPublicVolumeApiHardening()) {
-                // automotive hardening flag disabled, no blocking on auto
-                return false;
-            }
-            if (mContext.checkCallingOrSelfPermission(
-                    Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
-                    == PackageManager.PERMISSION_GRANTED) {
-                return false;
-            }
-            if (Binder.getCallingUid() < UserHandle.AID_APP_START) {
-                return false;
-            }
-            // TODO metrics?
-            // TODO log for audio dumpsys?
-            Log.e(TAG, "Preventing volume method " + volumeMethod + " for "
-                    + getPackNameForUid(Binder.getCallingUid()));
-            return true;
-        }
-        // not blocking
-        return false;
-    }
-
-    private String getPackNameForUid(int uid) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            final String[] names = mContext.getPackageManager().getPackagesForUid(uid);
-            if (names == null
-                    || names.length == 0
-                    || TextUtils.isEmpty(names[0])) {
-                return "[" + uid + "]";
-            }
-            return names[0];
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index ea92154..61e4f36 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -19,6 +19,9 @@
 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES;
 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN;
 import static android.media.AudioSystem.isBluetoothDevice;
+import static android.media.AudioSystem.isBluetoothLeDevice;
+
+import static com.android.media.audio.Flags.dsaOverBtLeAudio;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1625,10 +1628,10 @@
     }
 
     private int getHeadSensorHandleUpdateTracker() {
-        int headHandle = -1;
+        Sensor htSensor = null;
         if (sRoutingDevices.isEmpty()) {
             logloge("getHeadSensorHandleUpdateTracker: no device, no head tracker");
-            return headHandle;
+            return  -1;
         }
         final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0);
         List<String> deviceAddresses = mAudioService.getDeviceAddresses(currentDevice);
@@ -1642,27 +1645,86 @@
         for (String address : deviceAddresses) {
             UUID routingDeviceUuid = UuidUtils.uuidFromAudioDeviceAttributes(
                     new AudioDeviceAttributes(currentDevice.getInternalType(), address));
-            for (Sensor sensor : sensors) {
-                final UUID uuid = sensor.getUuid();
-                if (uuid.equals(routingDeviceUuid)) {
-                    headHandle = sensor.getHandle();
-                    if (!setHasHeadTracker(currentDevice)) {
-                        headHandle = -1;
+            if (dsaOverBtLeAudio()) {
+                for (Sensor sensor : sensors) {
+                    final UUID uuid = sensor.getUuid();
+                    if (uuid.equals(routingDeviceUuid)) {
+                        htSensor = sensor;
+                        HeadtrackerInfo info = new HeadtrackerInfo(sensor);
+                        if (isBluetoothLeDevice(currentDevice.getInternalType())) {
+                            if (info.getMajorVersion() == 2) {
+                                // Version 2 is used only by LE Audio profile
+                                break;
+                            }
+                            // we do not break, as this could be a match on the A2DP sensor
+                            // for a dual mode headset.
+                        } else if (info.getMajorVersion() == 1) {
+                            // Version 1 is used only by A2DP profile
+                            break;
+                        }
                     }
+                    if (htSensor == null && uuid.equals(UuidUtils.STANDALONE_UUID)) {
+                        htSensor = sensor;
+                        // we do not break, perhaps we find a head tracker on device.
+                    }
+                }
+                if (htSensor != null) {
+                    if (htSensor.getUuid().equals(UuidUtils.STANDALONE_UUID)) {
+                        break;
+                    }
+                    if (setHasHeadTracker(currentDevice)) {
+                        break;
+                    } else {
+                        htSensor = null;
+                    }
+                }
+            } else {
+                for (Sensor sensor : sensors) {
+                    final UUID uuid = sensor.getUuid();
+                    if (uuid.equals(routingDeviceUuid)) {
+                        htSensor = sensor;
+                        if (!setHasHeadTracker(currentDevice)) {
+                            htSensor = null;
+                        }
+                        break;
+                    }
+                    if (uuid.equals(UuidUtils.STANDALONE_UUID)) {
+                        htSensor = sensor;
+                        // we do not break, perhaps we find a head tracker on device.
+                    }
+                }
+                if (htSensor != null) {
                     break;
                 }
-                if (uuid.equals(UuidUtils.STANDALONE_UUID)) {
-                    headHandle = sensor.getHandle();
-                    // we do not break, perhaps we find a head tracker on device.
-                }
-            }
-            if (headHandle != -1) {
-                break;
             }
         }
-        return headHandle;
+        return htSensor != null ? htSensor.getHandle() : -1;
     }
 
+    /**
+     * Contains the information parsed from the head tracker sensor version.
+     * See platform/hardware/libhardware/modules/sensors/dynamic_sensor/HidRawSensor.h
+     * for the definition of version and capability fields.
+     */
+    private static class HeadtrackerInfo {
+        private final int mVersion;
+        HeadtrackerInfo(Sensor sensor) {
+            mVersion = sensor.getVersion();
+        }
+        int getMajorVersion() {
+            return (mVersion & 0xFF000000) >> 24;
+        }
+        int getMinorVersion() {
+            return (mVersion & 0xFF0000) >> 16;
+        }
+        boolean hasAclTransport() {
+            return getMajorVersion() == 2 ? ((mVersion & 0x1) != 0) : false;
+        }
+        boolean hasIsoTransport() {
+            return getMajorVersion() == 2 ? ((mVersion & 0x2) != 0) : false;
+        }
+    };
+
     private int getScreenSensorHandle() {
         int screenHandle = -1;
         Sensor screenSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index b9ccbfb..c507300 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -576,7 +576,7 @@
     }
 
     void onDialogAnimatedIn(boolean startFingerprintNow) {
-        if (mState != STATE_AUTH_STARTED) {
+        if (mState != STATE_AUTH_STARTED && mState != STATE_ERROR_PENDING_SYSUI) {
             Slog.e(TAG, "onDialogAnimatedIn, unexpected state: " + mState);
             return;
         }
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index c629b2b..be78ea2 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -157,4 +157,10 @@
      * @see VirtualDevice#getPersistentDeviceId()
      */
     public abstract @Nullable String getPersistentIdForDevice(int deviceId);
+
+    /**
+     * Returns all current persistent device IDs, including the ones for which no virtual device
+     * exists, as long as one may have existed or can be created.
+     */
+    public abstract @NonNull Set<String> getAllPersistentDeviceIds();
 }
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index fac727f..dff14b5 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -1114,12 +1114,22 @@
 
         public void notifyDeviceStateInfoAsync(@NonNull DeviceStateInfo info) {
             mHandler.post(() -> {
+                boolean tracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER);
+                if (tracingEnabled) { // To avoid creating the string when not needed.
+                    Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
+                            "notifyDeviceStateInfoAsync(pid=" + mPid + ")");
+                }
                 try {
                     mCallback.onDeviceStateInfoChanged(info);
                 } catch (RemoteException ex) {
                     Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.",
                             ex);
                 }
+                finally {
+                    if (tracingEnabled) {
+                        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+                    }
+                }
             });
         }
 
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index 6695801..9fcaa1e 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -38,6 +38,7 @@
     private final boolean mShouldUseAutoBrightness;
 
     private final boolean mIsSlowChange;
+    private final boolean mShouldUpdateScreenBrightnessSetting;
 
     private final float mCustomAnimationRate;
 
@@ -50,6 +51,7 @@
         mIsSlowChange = builder.isSlowChange();
         mMaxBrightness = builder.getMaxBrightness();
         mCustomAnimationRate = builder.getCustomAnimationRate();
+        mShouldUpdateScreenBrightnessSetting = builder.shouldUpdateScreenBrightnessSetting();
     }
 
     /**
@@ -109,6 +111,13 @@
         return mCustomAnimationRate;
     }
 
+    /**
+     * @return {@code true} if the screen brightness setting should be updated
+     */
+    public boolean shouldUpdateScreenBrightnessSetting() {
+        return mShouldUpdateScreenBrightnessSetting;
+    }
+
     @Override
     public String toString() {
         StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:");
@@ -123,6 +132,8 @@
         stringBuilder.append("\n    isSlowChange:").append(mIsSlowChange);
         stringBuilder.append("\n    maxBrightness:").append(mMaxBrightness);
         stringBuilder.append("\n    customAnimationRate:").append(mCustomAnimationRate);
+        stringBuilder.append("\n    shouldUpdateScreenBrightnessSetting:")
+                .append(mShouldUpdateScreenBrightnessSetting);
         return stringBuilder.toString();
     }
 
@@ -149,13 +160,16 @@
                 && mShouldUseAutoBrightness == otherState.getShouldUseAutoBrightness()
                 && mIsSlowChange == otherState.isSlowChange()
                 && mMaxBrightness == otherState.getMaxBrightness()
-                && mCustomAnimationRate == otherState.getCustomAnimationRate();
+                && mCustomAnimationRate == otherState.getCustomAnimationRate()
+                && mShouldUpdateScreenBrightnessSetting
+                    == otherState.shouldUpdateScreenBrightnessSetting();
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason,
-                mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mCustomAnimationRate);
+                mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mCustomAnimationRate,
+                mShouldUpdateScreenBrightnessSetting);
     }
 
     /**
@@ -177,6 +191,7 @@
         private boolean mIsSlowChange;
         private float mMaxBrightness;
         private float mCustomAnimationRate = CUSTOM_ANIMATION_RATE_NOT_SET;
+        private boolean mShouldUpdateScreenBrightnessSetting;
 
         /**
          * Create a builder starting with the values from the specified {@link
@@ -194,6 +209,8 @@
             builder.setIsSlowChange(state.isSlowChange());
             builder.setMaxBrightness(state.getMaxBrightness());
             builder.setCustomAnimationRate(state.getCustomAnimationRate());
+            builder.setShouldUpdateScreenBrightnessSetting(
+                    state.shouldUpdateScreenBrightnessSetting());
             return builder;
         }
 
@@ -290,8 +307,8 @@
         /**
          * See {@link DisplayBrightnessState#isSlowChange()}.
          */
-        public Builder setIsSlowChange(boolean shouldUseAutoBrightness) {
-            this.mIsSlowChange = shouldUseAutoBrightness;
+        public Builder setIsSlowChange(boolean isSlowChange) {
+            this.mIsSlowChange = isSlowChange;
             return this;
         }
 
@@ -334,6 +351,22 @@
         }
 
         /**
+         * See {@link DisplayBrightnessState#shouldUpdateScreenBrightnessSetting()}.
+         */
+        public boolean shouldUpdateScreenBrightnessSetting() {
+            return mShouldUpdateScreenBrightnessSetting;
+        }
+
+        /**
+         * See {@link DisplayBrightnessState#shouldUpdateScreenBrightnessSetting()}.
+         */
+        public Builder setShouldUpdateScreenBrightnessSetting(
+                boolean shouldUpdateScreenBrightnessSetting) {
+            mShouldUpdateScreenBrightnessSetting = shouldUpdateScreenBrightnessSetting;
+            return this;
+        }
+
+        /**
          * This is used to construct an immutable DisplayBrightnessState object from its builder
          */
         public DisplayBrightnessState build() {
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 9f4f787..2fdf90d 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -209,7 +209,7 @@
             int state,
             float brightnessState,
             float sdrBrightnessState,
-            @Nullable DisplayOffloadSession displayOffloadSession) {
+            @Nullable DisplayOffloadSessionImpl displayOffloadSession) {
         return null;
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 11f4e5f..e99f82a 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -4948,20 +4948,8 @@
                     return null;
                 }
 
-                DisplayOffloadSession session =
-                        new DisplayOffloadSession() {
-                            @Override
-                            public void setDozeStateOverride(int displayState) {
-                                synchronized (mSyncRoot) {
-                                    displayPowerController.overrideDozeScreenState(displayState);
-                                }
-                            }
-
-                            @Override
-                            public DisplayOffloader getDisplayOffloader() {
-                                return displayOffloader;
-                            }
-                        };
+                DisplayOffloadSessionImpl session = new DisplayOffloadSessionImpl(displayOffloader,
+                        displayPowerController);
                 logicalDisplay.setDisplayOffloadSessionLocked(session);
                 displayPowerController.setDisplayOffloadSession(session);
                 return session;
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
new file mode 100644
index 0000000..1bd556b
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
+import android.os.Trace;
+
+/**
+ * An implementation of the offload session that keeps track of whether the session is active.
+ * An offload session is used to control the display's brightness using the offload chip.
+ */
+public class DisplayOffloadSessionImpl implements DisplayManagerInternal.DisplayOffloadSession {
+
+    @Nullable
+    private final DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
+    private final DisplayPowerControllerInterface mDisplayPowerController;
+    private boolean mIsActive;
+
+    public DisplayOffloadSessionImpl(
+            @Nullable DisplayManagerInternal.DisplayOffloader displayOffloader,
+            DisplayPowerControllerInterface displayPowerController) {
+        mDisplayOffloader = displayOffloader;
+        mDisplayPowerController = displayPowerController;
+    }
+
+    @Override
+    public void setDozeStateOverride(int displayState) {
+        mDisplayPowerController.overrideDozeScreenState(displayState);
+    }
+
+    @Override
+    public boolean isActive() {
+        return mIsActive;
+    }
+
+    @Override
+    public void updateBrightness(float brightness) {
+        if (mIsActive) {
+            mDisplayPowerController.setBrightnessFromOffload(brightness);
+        }
+    }
+
+    /**
+     * Start the offload session. The method returns if the session is already active.
+     * @return Whether the session was started successfully
+     */
+    public boolean startOffload() {
+        if (mDisplayOffloader == null || mIsActive) {
+            return false;
+        }
+        Trace.traceBegin(Trace.TRACE_TAG_POWER, "DisplayOffloader#startOffload");
+        try {
+            return mIsActive = mDisplayOffloader.startOffload();
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_POWER);
+        }
+    }
+
+    /**
+     * Stop the offload session. The method returns if the session is not active.
+     */
+    public void stopOffload() {
+        if (mDisplayOffloader == null || !mIsActive) {
+            return;
+        }
+        Trace.traceBegin(Trace.TRACE_TAG_POWER, "DisplayOffloader#stopOffload");
+        try {
+            mDisplayOffloader.stopOffload();
+            mIsActive = false;
+            mDisplayPowerController.setBrightnessFromOffload(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_POWER);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 5761c31..f3d761a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -2224,6 +2224,11 @@
     }
 
     @Override
+    public void setBrightnessFromOffload(float brightness) {
+        // The old DPC is no longer supported
+    }
+
+    @Override
     public BrightnessInfo getBrightnessInfo() {
         synchronized (mCachedBrightnessInfo) {
             return new BrightnessInfo(
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index a6155da..d4e0cbb 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -151,6 +151,7 @@
     private static final int MSG_SET_DWBC_STRONG_MODE = 14;
     private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15;
     private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16;
+    private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17;
 
 
 
@@ -562,7 +563,7 @@
                 new DisplayBrightnessController(context, null,
                         mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault,
                         brightnessSetting, () -> postBrightnessChangeRunnable(),
-                        new HandlerExecutor(mHandler));
+                        new HandlerExecutor(mHandler), flags);
 
         mBrightnessClamperController = mInjector.getBrightnessClamperController(
                 mHandler, modeChangeCallback::run,
@@ -1394,7 +1395,8 @@
                         ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
                         : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED);
 
-        boolean updateScreenBrightnessSetting = false;
+        boolean updateScreenBrightnessSetting =
+                displayBrightnessState.shouldUpdateScreenBrightnessSetting();
         float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness();
         // Apply auto-brightness.
         int brightnessAdjustmentFlags = 0;
@@ -1854,6 +1856,13 @@
     }
 
     @Override
+    public void setBrightnessFromOffload(float brightness) {
+        Message msg = mHandler.obtainMessage(MSG_SET_BRIGHTNESS_FROM_OFFLOAD,
+                Float.floatToIntBits(brightness), 0 /*unused*/);
+        mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+    }
+
+    @Override
     public BrightnessInfo getBrightnessInfo() {
         synchronized (mCachedBrightnessInfo) {
             return new BrightnessInfo(
@@ -2886,6 +2895,11 @@
                 case MSG_SET_DWBC_LOGGING_ENABLED:
                     setDwbcLoggingEnabled(msg.arg1);
                     break;
+                case MSG_SET_BRIGHTNESS_FROM_OFFLOAD:
+                    mDisplayBrightnessController.setBrightnessFromOffload(
+                            Float.intBitsToFloat(msg.arg1));
+                    updatePowerState();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index 181386a..72079a4 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -22,6 +22,7 @@
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
 
 import java.io.PrintWriter;
 
@@ -150,6 +151,14 @@
     void setTemporaryAutoBrightnessAdjustment(float adjustment);
 
     /**
+     * Sets temporary brightness from the offload chip until we get a brightness value from
+     * the light sensor.
+     * @param brightness The brightness value between {@link PowerManager.BRIGHTNESS_MIN} and
+     * {@link PowerManager.BRIGHTNESS_MAX}. Values outside of that range will be ignored.
+     */
+    void setBrightnessFromOffload(float brightness);
+
+    /**
      * Gets the screen brightness setting
      */
     float getScreenBrightnessSetting();
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index be3207d..ff9a1ab 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -19,11 +19,11 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.Mode.INVALID_MODE_ID;
 
+import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.content.Context;
 import android.content.res.Resources;
 import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
-import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
 import android.hardware.sidekick.SidekickInternal;
 import android.os.Build;
 import android.os.Handler;
@@ -238,7 +238,6 @@
         private boolean mAllmRequested;
         private boolean mGameContentTypeRequested;
         private boolean mSidekickActive;
-        private boolean mDisplayOffloadActive;
         private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo;
         // The supported display modes according to SurfaceFlinger
         private SurfaceControl.DisplayMode[] mSfDisplayModes;
@@ -765,7 +764,7 @@
                 final int state,
                 final float brightnessState,
                 final float sdrBrightnessState,
-                DisplayOffloadSession displayOffloadSession) {
+                @Nullable DisplayOffloadSessionImpl displayOffloadSession) {
 
             // Assume that the brightness is off if the display is being turned off.
             assert state != Display.STATE_OFF
@@ -832,25 +831,13 @@
                                     + ", state=" + Display.stateToString(state) + ")");
                         }
 
-                        DisplayOffloader displayOffloader =
-                                displayOffloadSession == null
-                                        ? null
-                                        : displayOffloadSession.getDisplayOffloader();
-
                         boolean isDisplayOffloadEnabled = mFlags.isDisplayOffloadEnabled();
 
                         // We must tell sidekick/displayoffload to stop controlling the display
                         // before we can change its power mode, so do that first.
                         if (isDisplayOffloadEnabled) {
-                            if (mDisplayOffloadActive && displayOffloader != null) {
-                                Trace.traceBegin(Trace.TRACE_TAG_POWER,
-                                        "DisplayOffloader#stopOffload");
-                                try {
-                                    displayOffloader.stopOffload();
-                                } finally {
-                                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
-                                }
-                                mDisplayOffloadActive = false;
+                            if (displayOffloadSession != null) {
+                                displayOffloadSession.stopOffload();
                             }
                         } else {
                             if (mSidekickActive) {
@@ -881,16 +868,9 @@
                         // have a sidekick/displayoffload available, tell it now that it can take
                         // control.
                         if (isDisplayOffloadEnabled) {
-                            if (DisplayOffloadSession.isSupportedOffloadState(state) &&
-                                    displayOffloader != null
-                                    && !mDisplayOffloadActive) {
-                                Trace.traceBegin(
-                                        Trace.TRACE_TAG_POWER, "DisplayOffloader#startOffload");
-                                try {
-                                    mDisplayOffloadActive = displayOffloader.startOffload();
-                                } finally {
-                                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
-                                }
+                            if (DisplayOffloadSession.isSupportedOffloadState(state)
+                                    && displayOffloadSession != null) {
+                                displayOffloadSession.startOffload();
                             }
                         } else {
                             if (Display.isSuspendedState(state) && state != Display.STATE_OFF
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 3d4209e..ba321ae 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -138,7 +138,7 @@
     private final Rect mTempDisplayRect = new Rect();
 
     /** A session token that controls the offloading operations of this logical display. */
-    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+    private DisplayOffloadSessionImpl mDisplayOffloadSession;
 
     /**
      * Name of a display group to which the display is assigned.
@@ -969,12 +969,11 @@
         return mDisplayGroupName;
     }
 
-    public void setDisplayOffloadSessionLocked(
-            DisplayManagerInternal.DisplayOffloadSession session) {
+    public void setDisplayOffloadSessionLocked(DisplayOffloadSessionImpl session) {
         mDisplayOffloadSession = session;
     }
 
-    public DisplayManagerInternal.DisplayOffloadSession getDisplayOffloadSessionLocked() {
+    public DisplayOffloadSessionImpl getDisplayOffloadSessionLocked() {
         return mDisplayOffloadSession;
     }
 
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 90e32a6..edbd424 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -43,7 +43,6 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Point;
-import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.display.VirtualDisplayConfig;
 import android.media.projection.IMediaProjection;
@@ -380,7 +379,7 @@
 
         @Override
         public Runnable requestDisplayStateLocked(int state, float brightnessState,
-                float sdrBrightnessState, DisplayOffloadSession displayOffloadSession) {
+                float sdrBrightnessState, DisplayOffloadSessionImpl displayOffloadSession) {
             if (state != mDisplayState) {
                 mDisplayState = state;
                 if (state == Display.STATE_OFF) {
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
index d7ae269..8fe5f21 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
@@ -39,7 +39,8 @@
     public static final int REASON_BOOST = 8;
     public static final int REASON_SCREEN_OFF_BRIGHTNESS_SENSOR = 9;
     public static final int REASON_FOLLOWER = 10;
-    public static final int REASON_MAX = REASON_FOLLOWER;
+    public static final int REASON_OFFLOAD = 11;
+    public static final int REASON_MAX = REASON_OFFLOAD;
 
     public static final int MODIFIER_DIMMED = 0x1;
     public static final int MODIFIER_LOW_POWER = 0x2;
@@ -196,6 +197,8 @@
                 return "screen_off_brightness_sensor";
             case REASON_FOLLOWER:
                 return "follower";
+            case REASON_OFFLOAD:
+                return "offload";
             default:
                 return Integer.toString(reason);
         }
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index d6f0098..617befb 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -31,6 +31,7 @@
 import com.android.server.display.DisplayBrightnessState;
 import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
 import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import java.io.PrintWriter;
 
@@ -104,7 +105,8 @@
      */
     public DisplayBrightnessController(Context context, Injector injector, int displayId,
             float defaultScreenBrightness, BrightnessSetting brightnessSetting,
-            Runnable onBrightnessChangeRunnable, HandlerExecutor brightnessChangeExecutor) {
+            Runnable onBrightnessChangeRunnable, HandlerExecutor brightnessChangeExecutor,
+            DisplayManagerFlags flags) {
         if (injector == null) {
             injector = new Injector();
         }
@@ -116,7 +118,7 @@
         mCurrentScreenBrightness = getScreenBrightnessSetting();
         mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
         mDisplayBrightnessStrategySelector = injector.getDisplayBrightnessStrategySelector(context,
-                displayId);
+                displayId, flags);
         mBrightnessChangeExecutor = brightnessChangeExecutor;
         mPersistBrightnessNitsForDefaultDisplay = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay);
@@ -172,6 +174,18 @@
     }
 
     /**
+     * Sets the brightness from the offload session.
+     */
+    public void setBrightnessFromOffload(float brightness) {
+        synchronized (mLock) {
+            if (mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy() != null) {
+                mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy()
+                        .setOffloadScreenBrightness(brightness);
+            }
+        }
+    }
+
+    /**
      * Returns a boolean flag indicating if the light sensor is to be used to decide the screen
      * brightness when dozing
      */
@@ -423,8 +437,9 @@
     @VisibleForTesting
     static class Injector {
         DisplayBrightnessStrategySelector getDisplayBrightnessStrategySelector(Context context,
-                int displayId) {
-            return new DisplayBrightnessStrategySelector(context, /* injector= */ null, displayId);
+                int displayId, DisplayManagerFlags flags) {
+            return new DisplayBrightnessStrategySelector(context, /* injector= */ null, displayId,
+                    flags);
         }
     }
 
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index f141c20..055f94a 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -17,6 +17,7 @@
 package com.android.server.display.brightness;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.display.DisplayManagerInternal;
 import android.util.IndentingPrintWriter;
@@ -31,9 +32,11 @@
 import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
 import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
 import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
+import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
 import com.android.server.display.brightness.strategy.OverrideBrightnessStrategy;
 import com.android.server.display.brightness.strategy.ScreenOffBrightnessStrategy;
 import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import java.io.PrintWriter;
 
@@ -63,6 +66,10 @@
     private final InvalidBrightnessStrategy mInvalidBrightnessStrategy;
     // Controls brightness when automatic (adaptive) brightness is running.
     private final AutomaticBrightnessStrategy mAutomaticBrightnessStrategy;
+    // Controls the brightness if adaptive brightness is on and there exists an active offload
+    // session. Brightness value is provided by the offload session.
+    @Nullable
+    private final OffloadBrightnessStrategy mOffloadBrightnessStrategy;
 
     // We take note of the old brightness strategy so that we can know when the strategy changes.
     private String mOldBrightnessStrategyName;
@@ -72,7 +79,8 @@
     /**
      * The constructor of DozeBrightnessStrategy.
      */
-    public DisplayBrightnessStrategySelector(Context context, Injector injector, int displayId) {
+    public DisplayBrightnessStrategySelector(Context context, Injector injector, int displayId,
+            DisplayManagerFlags flags) {
         if (injector == null) {
             injector = new Injector();
         }
@@ -85,6 +93,11 @@
         mFollowerBrightnessStrategy = injector.getFollowerBrightnessStrategy(displayId);
         mInvalidBrightnessStrategy = injector.getInvalidBrightnessStrategy();
         mAutomaticBrightnessStrategy = injector.getAutomaticBrightnessStrategy(context, displayId);
+        if (flags.isDisplayOffloadEnabled()) {
+            mOffloadBrightnessStrategy = injector.getOffloadBrightnessStrategy();
+        } else {
+            mOffloadBrightnessStrategy = null;
+        }
         mAllowAutoBrightnessWhileDozingConfig = context.getResources().getBoolean(
                 R.bool.config_allowAutoBrightnessWhileDozing);
         mOldBrightnessStrategyName = mInvalidBrightnessStrategy.getName();
@@ -114,6 +127,9 @@
         } else if (BrightnessUtils.isValidBrightnessValue(
                 mTemporaryBrightnessStrategy.getTemporaryScreenBrightness())) {
             displayBrightnessStrategy = mTemporaryBrightnessStrategy;
+        } else if (mOffloadBrightnessStrategy != null && BrightnessUtils.isValidBrightnessValue(
+                mOffloadBrightnessStrategy.getOffloadScreenBrightness())) {
+            displayBrightnessStrategy = mOffloadBrightnessStrategy;
         }
 
         if (!mOldBrightnessStrategyName.equals(displayBrightnessStrategy.getName())) {
@@ -138,6 +154,11 @@
         return mAutomaticBrightnessStrategy;
     }
 
+    @Nullable
+    public OffloadBrightnessStrategy getOffloadBrightnessStrategy() {
+        return mOffloadBrightnessStrategy;
+    }
+
     /**
      * Returns a boolean flag indicating if the light sensor is to be used to decide the screen
      * brightness when dozing
@@ -159,6 +180,9 @@
                         + mAllowAutoBrightnessWhileDozingConfig);
         IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
         mTemporaryBrightnessStrategy.dump(ipw);
+        if (mOffloadBrightnessStrategy != null) {
+            mOffloadBrightnessStrategy.dump(ipw);
+        }
     }
 
     /**
@@ -210,5 +234,9 @@
         AutomaticBrightnessStrategy getAutomaticBrightnessStrategy(Context context, int displayId) {
             return new AutomaticBrightnessStrategy(context, displayId);
         }
+
+        OffloadBrightnessStrategy getOffloadBrightnessStrategy() {
+            return new OffloadBrightnessStrategy();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index bcd5259..3c23b5c 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -107,6 +107,7 @@
         mIsAutoBrightnessEnabled = shouldUseAutoBrightness()
                 && (targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze)
                 && brightnessReason != BrightnessReason.REASON_OVERRIDE
+                && brightnessReason != BrightnessReason.REASON_OFFLOAD
                 && mAutomaticBrightnessController != null;
         mAutoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness()
                 && !(targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze);
diff --git a/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java
new file mode 100644
index 0000000..55f8914
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages the brightness of the display when auto-brightness is on, the screen has just turned on
+ * and there is no available lux reading yet. The brightness value is read from the offload chip.
+ */
+public class OffloadBrightnessStrategy implements DisplayBrightnessStrategy {
+
+    private float mOffloadScreenBrightness;
+
+    public OffloadBrightnessStrategy() {
+        mOffloadScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+    }
+
+    @Override
+    public DisplayBrightnessState updateBrightness(
+            DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
+        BrightnessReason brightnessReason = new BrightnessReason();
+        brightnessReason.setReason(BrightnessReason.REASON_OFFLOAD);
+        return new DisplayBrightnessState.Builder()
+                .setBrightness(mOffloadScreenBrightness)
+                .setSdrBrightness(mOffloadScreenBrightness)
+                .setBrightnessReason(brightnessReason)
+                .setDisplayBrightnessStrategyName(getName())
+                .setIsSlowChange(false)
+                .setShouldUpdateScreenBrightnessSetting(true)
+                .build();
+    }
+
+    @Override
+    public String getName() {
+        return "OffloadBrightnessStrategy";
+    }
+
+    public float getOffloadScreenBrightness() {
+        return mOffloadScreenBrightness;
+    }
+
+    public void setOffloadScreenBrightness(float offloadScreenBrightness) {
+        mOffloadScreenBrightness = offloadScreenBrightness;
+    }
+
+    /**
+     * Dumps the state of this class.
+     */
+    public void dump(PrintWriter writer) {
+        writer.println("OffloadBrightnessStrategy:");
+        writer.println("  mOffloadScreenBrightness:" + mOffloadScreenBrightness);
+    }
+}
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 360a6a7..6bdfae2 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -110,7 +110,7 @@
 
     @Override
     @NonNull
-    public synchronized MediaRoute2Info getDeviceRoute() {
+    public synchronized MediaRoute2Info getSelectedRoute() {
         if (mSelectedRoute != null) {
             return mSelectedRoute;
         }
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 7876095..0fdaaa7 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -72,13 +72,9 @@
      */
     boolean selectRoute(@Nullable @MediaRoute2Info.Type Integer type);
 
-    /**
-     * Returns currently selected device (built-in or wired) route.
-     *
-     * @return non-null device route.
-     */
+    /** Returns the currently selected device (built-in or wired) route. */
     @NonNull
-    MediaRoute2Info getDeviceRoute();
+    MediaRoute2Info getSelectedRoute();
 
     /**
      * Updates device route volume.
diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
index 6ba40ae..65874e2 100644
--- a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
@@ -107,7 +107,7 @@
 
     @Override
     @NonNull
-    public synchronized MediaRoute2Info getDeviceRoute() {
+    public synchronized MediaRoute2Info getSelectedRoute() {
         return mDeviceRoute;
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index c24d6a0..994d3ca 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -76,6 +76,7 @@
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Slog;
 import android.view.KeyEvent;
 
 import com.android.server.LocalServices;
@@ -348,16 +349,19 @@
         } else {
             if (mVolumeControlType == VOLUME_CONTROL_FIXED) {
                 if (DEBUG) {
-                    Log.d(TAG, "Session does not support volume adjustment");
+                    Slog.d(TAG, "Session does not support volume adjustment");
                 }
             } else if (direction == AudioManager.ADJUST_TOGGLE_MUTE
                     || direction == AudioManager.ADJUST_MUTE
                     || direction == AudioManager.ADJUST_UNMUTE) {
-                Log.w(TAG, "Muting remote playback is not supported");
+                Slog.w(TAG, "Muting remote playback is not supported");
             } else {
                 if (DEBUG) {
-                    Log.w(TAG, "adjusting volume, pkg=" + packageName + ", asSystemService="
-                            + asSystemService + ", dir=" + direction);
+                    Slog.w(
+                            TAG,
+                            "adjusting volume, pkg=" + packageName
+                                    + ", asSystemService=" + asSystemService
+                                    + ", dir=" + direction);
                 }
                 mSessionCb.adjustVolume(packageName, pid, uid, asSystemService, direction);
 
@@ -371,8 +375,10 @@
                 }
 
                 if (DEBUG) {
-                    Log.d(TAG, "Adjusted optimistic volume to " + mOptimisticVolume + " max is "
-                            + mMaxVolume);
+                    Slog.d(
+                            TAG,
+                            "Adjusted optimistic volume to " + mOptimisticVolume
+                                    + " max is " + mMaxVolume);
                 }
             }
             // Always notify, even if the volume hasn't changed. This is important to ensure that
@@ -388,23 +394,33 @@
         if (mVolumeType == PLAYBACK_TYPE_LOCAL) {
             int stream = getVolumeStream(mAudioAttrs);
             final int volumeValue = value;
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        mAudioManager.setStreamVolumeForUid(stream, volumeValue, flags,
-                                opPackageName, uid, pid,
-                                mContext.getApplicationInfo().targetSdkVersion);
-                    } catch (IllegalArgumentException | SecurityException e) {
-                        Log.e(TAG, "Cannot set volume: stream=" + stream + ", value=" + volumeValue
-                                + ", flags=" + flags, e);
-                    }
-                }
-            });
+            mHandler.post(
+                    new Runnable() {
+                        @Override
+                        public void run() {
+                            try {
+                                mAudioManager.setStreamVolumeForUid(
+                                        stream,
+                                        volumeValue,
+                                        flags,
+                                        opPackageName,
+                                        uid,
+                                        pid,
+                                        mContext.getApplicationInfo().targetSdkVersion);
+                            } catch (IllegalArgumentException | SecurityException e) {
+                                Slog.e(
+                                        TAG,
+                                        "Cannot set volume: stream=" + stream
+                                                + ", value=" + volumeValue
+                                                + ", flags=" + flags,
+                                        e);
+                            }
+                        }
+                    });
         } else {
             if (mVolumeControlType != VOLUME_CONTROL_ABSOLUTE) {
                 if (DEBUG) {
-                    Log.d(TAG, "Session does not support setting volume");
+                    Slog.d(TAG, "Session does not support setting volume");
                 }
             } else {
                 value = Math.max(0, Math.min(value, mMaxVolume));
@@ -419,8 +435,10 @@
                 }
 
                 if (DEBUG) {
-                    Log.d(TAG, "Set optimistic volume to " + mOptimisticVolume + " max is "
-                            + mMaxVolume);
+                    Slog.d(
+                            TAG,
+                            "Set optimistic volume to " + mOptimisticVolume
+                                    + " max is " + mMaxVolume);
                 }
             }
             // Always notify, even if the volume hasn't changed.
@@ -528,25 +546,25 @@
     public boolean canHandleVolumeKey() {
         if (isPlaybackTypeLocal()) {
             if (DEBUG) {
-                Log.d(TAG, "Local MediaSessionRecord can handle volume key");
+                Slog.d(TAG, "Local MediaSessionRecord can handle volume key");
             }
             return true;
         }
         if (mVolumeControlType == VOLUME_CONTROL_FIXED) {
             if (DEBUG) {
-                Log.d(
+                Slog.d(
                         TAG,
                         "Local MediaSessionRecord with FIXED volume control can't handle volume"
-                            + " key");
+                                + " key");
             }
             return false;
         }
         if (mVolumeAdjustmentForRemoteGroupSessions) {
             if (DEBUG) {
-                Log.d(
+                Slog.d(
                         TAG,
                         "Volume adjustment for remote group sessions allowed so MediaSessionRecord"
-                            + " can handle volume key");
+                                + " can handle volume key");
             }
             return true;
         }
@@ -556,7 +574,7 @@
         boolean foundNonSystemSession = false;
         boolean remoteSessionAllowVolumeAdjustment = true;
         if (DEBUG) {
-            Log.d(
+            Slog.d(
                     TAG,
                     "Found "
                             + sessions.size()
@@ -565,7 +583,7 @@
         }
         for (RoutingSessionInfo session : sessions) {
             if (DEBUG) {
-                Log.d(TAG, "Found routingSessionInfo: " + session);
+                Slog.d(TAG, "Found routingSessionInfo: " + session);
             }
             if (!session.isSystemSession()) {
                 foundNonSystemSession = true;
@@ -576,10 +594,9 @@
         }
         if (!foundNonSystemSession) {
             if (DEBUG) {
-                Log.d(
+                Slog.d(
                         TAG,
-                        "Package "
-                                + mPackageName
+                        "Package " + mPackageName
                                 + " has a remote media session but no associated routing session");
             }
         }
@@ -669,8 +686,11 @@
             final boolean asSystemService, final boolean useSuggested,
             final int previousFlagPlaySound) {
         if (DEBUG) {
-            Log.w(TAG, "adjusting local volume, stream=" + stream + ", dir=" + direction
-                    + ", asSystemService=" + asSystemService + ", useSuggested=" + useSuggested);
+            Slog.w(
+                    TAG,
+                    "adjusting local volume, stream=" + stream + ", dir=" + direction
+                            + ", asSystemService=" + asSystemService
+                            + ", useSuggested=" + useSuggested);
         }
         // Must use opPackageName for adjusting volumes with UID.
         final String opPackageName;
@@ -685,40 +705,61 @@
             uid = callingUid;
             pid = callingPid;
         }
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    if (useSuggested) {
-                        if (AudioSystem.isStreamActive(stream, 0)) {
-                            mAudioManager.adjustSuggestedStreamVolumeForUid(stream,
-                                    direction, flags, opPackageName, uid, pid,
-                                    mContext.getApplicationInfo().targetSdkVersion);
-                        } else {
-                            mAudioManager.adjustSuggestedStreamVolumeForUid(
-                                    AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
-                                    flags | previousFlagPlaySound, opPackageName, uid, pid,
-                                    mContext.getApplicationInfo().targetSdkVersion);
+        mHandler.post(
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            if (useSuggested) {
+                                if (AudioSystem.isStreamActive(stream, 0)) {
+                                    mAudioManager.adjustSuggestedStreamVolumeForUid(
+                                            stream,
+                                            direction,
+                                            flags,
+                                            opPackageName,
+                                            uid,
+                                            pid,
+                                            mContext.getApplicationInfo().targetSdkVersion);
+                                } else {
+                                    mAudioManager.adjustSuggestedStreamVolumeForUid(
+                                            AudioManager.USE_DEFAULT_STREAM_TYPE,
+                                            direction,
+                                            flags | previousFlagPlaySound,
+                                            opPackageName,
+                                            uid,
+                                            pid,
+                                            mContext.getApplicationInfo().targetSdkVersion);
+                                }
+                            } else {
+                                mAudioManager.adjustStreamVolumeForUid(
+                                        stream,
+                                        direction,
+                                        flags,
+                                        opPackageName,
+                                        uid,
+                                        pid,
+                                        mContext.getApplicationInfo().targetSdkVersion);
+                            }
+                        } catch (IllegalArgumentException | SecurityException e) {
+                            Slog.e(
+                                    TAG,
+                                    "Cannot adjust volume: direction=" + direction
+                                            + ", stream=" + stream + ", flags=" + flags
+                                            + ", opPackageName=" + opPackageName + ", uid=" + uid
+                                            + ", useSuggested=" + useSuggested
+                                            + ", previousFlagPlaySound=" + previousFlagPlaySound,
+                                    e);
                         }
-                    } else {
-                        mAudioManager.adjustStreamVolumeForUid(stream, direction, flags,
-                                opPackageName, uid, pid,
-                                mContext.getApplicationInfo().targetSdkVersion);
                     }
-                } catch (IllegalArgumentException | SecurityException e) {
-                    Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
-                            + stream + ", flags=" + flags + ", opPackageName=" + opPackageName
-                            + ", uid=" + uid + ", useSuggested=" + useSuggested
-                            + ", previousFlagPlaySound=" + previousFlagPlaySound, e);
-                }
-            }
-        });
+                });
     }
 
     private void logCallbackException(
             String msg, ISessionControllerCallbackHolder holder, Exception e) {
-        Log.v(TAG, msg + ", this=" + this + ", callback package=" + holder.mPackageName
-                + ", exception=" + e);
+        Slog.v(
+                TAG,
+                msg + ", this=" + this + ", callback package=" + holder.mPackageName
+                        + ", exception=" + e);
     }
 
     private void pushPlaybackStateUpdate() {
@@ -1115,7 +1156,9 @@
                         throw new IllegalArgumentException(
                                 "The media button receiver cannot be set to an activity.");
                     } else {
-                        Log.w(TAG, "Ignoring invalid media button receiver targeting an activity.");
+                        Slog.w(
+                                TAG,
+                                "Ignoring invalid media button receiver targeting an activity.");
                         return;
                     }
                 }
@@ -1151,7 +1194,7 @@
                     if (CompatChanges.isChangeEnabled(THROW_FOR_INVALID_BROADCAST_RECEIVER, uid)) {
                         throw new IllegalArgumentException("Invalid component name: " + receiver);
                     } else {
-                        Log.w(
+                        Slog.w(
                                 TAG,
                                 "setMediaButtonBroadcastReceiver(): "
                                         + "Ignoring invalid component name="
@@ -1290,7 +1333,7 @@
                 if (attributes != null) {
                     mAudioAttrs = attributes;
                 } else {
-                    Log.e(TAG, "Received null audio attributes, using existing attributes");
+                    Slog.e(TAG, "Received null audio attributes, using existing attributes");
                 }
             }
             if (typeChanged) {
@@ -1352,7 +1395,7 @@
                 }
                 return true;
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in sendMediaRequest.", e);
+                Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
             }
             return false;
         }
@@ -1375,7 +1418,7 @@
                 }
                 return true;
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in sendMediaRequest.", e);
+                Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
             }
             return false;
         }
@@ -1388,7 +1431,7 @@
                         pid, uid, packageName, reason);
                 mCb.onCommand(packageName, pid, uid, command, args, cb);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in sendCommand.", e);
+                Slog.e(TAG, "Remote failure in sendCommand.", e);
             }
         }
 
@@ -1400,7 +1443,7 @@
                         pid, uid, packageName, reason);
                 mCb.onCustomAction(packageName, pid, uid, action, args);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in sendCustomAction.", e);
+                Slog.e(TAG, "Remote failure in sendCustomAction.", e);
             }
         }
 
@@ -1411,7 +1454,7 @@
                         pid, uid, packageName, reason);
                 mCb.onPrepare(packageName, pid, uid);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in prepare.", e);
+                Slog.e(TAG, "Remote failure in prepare.", e);
             }
         }
 
@@ -1423,7 +1466,7 @@
                         pid, uid, packageName, reason);
                 mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in prepareFromMediaId.", e);
+                Slog.e(TAG, "Remote failure in prepareFromMediaId.", e);
             }
         }
 
@@ -1435,7 +1478,7 @@
                         pid, uid, packageName, reason);
                 mCb.onPrepareFromSearch(packageName, pid, uid, query, extras);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in prepareFromSearch.", e);
+                Slog.e(TAG, "Remote failure in prepareFromSearch.", e);
             }
         }
 
@@ -1446,7 +1489,7 @@
                         pid, uid, packageName, reason);
                 mCb.onPrepareFromUri(packageName, pid, uid, uri, extras);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in prepareFromUri.", e);
+                Slog.e(TAG, "Remote failure in prepareFromUri.", e);
             }
         }
 
@@ -1457,7 +1500,7 @@
                         pid, uid, packageName, reason);
                 mCb.onPlay(packageName, pid, uid);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in play.", e);
+                Slog.e(TAG, "Remote failure in play.", e);
             }
         }
 
@@ -1469,7 +1512,7 @@
                         pid, uid, packageName, reason);
                 mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in playFromMediaId.", e);
+                Slog.e(TAG, "Remote failure in playFromMediaId.", e);
             }
         }
 
@@ -1481,7 +1524,7 @@
                         pid, uid, packageName, reason);
                 mCb.onPlayFromSearch(packageName, pid, uid, query, extras);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in playFromSearch.", e);
+                Slog.e(TAG, "Remote failure in playFromSearch.", e);
             }
         }
 
@@ -1492,7 +1535,7 @@
                         pid, uid, packageName, reason);
                 mCb.onPlayFromUri(packageName, pid, uid, uri, extras);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in playFromUri.", e);
+                Slog.e(TAG, "Remote failure in playFromUri.", e);
             }
         }
 
@@ -1503,7 +1546,7 @@
                         pid, uid, packageName, reason);
                 mCb.onSkipToTrack(packageName, pid, uid, id);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in skipToTrack", e);
+                Slog.e(TAG, "Remote failure in skipToTrack", e);
             }
         }
 
@@ -1514,7 +1557,7 @@
                         pid, uid, packageName, reason);
                 mCb.onPause(packageName, pid, uid);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in pause.", e);
+                Slog.e(TAG, "Remote failure in pause.", e);
             }
         }
 
@@ -1525,7 +1568,7 @@
                         pid, uid, packageName, reason);
                 mCb.onStop(packageName, pid, uid);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in stop.", e);
+                Slog.e(TAG, "Remote failure in stop.", e);
             }
         }
 
@@ -1536,7 +1579,7 @@
                         pid, uid, packageName, reason);
                 mCb.onNext(packageName, pid, uid);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in next.", e);
+                Slog.e(TAG, "Remote failure in next.", e);
             }
         }
 
@@ -1547,7 +1590,7 @@
                         pid, uid, packageName, reason);
                 mCb.onPrevious(packageName, pid, uid);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in previous.", e);
+                Slog.e(TAG, "Remote failure in previous.", e);
             }
         }
 
@@ -1558,7 +1601,7 @@
                         pid, uid, packageName, reason);
                 mCb.onFastForward(packageName, pid, uid);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in fastForward.", e);
+                Slog.e(TAG, "Remote failure in fastForward.", e);
             }
         }
 
@@ -1569,7 +1612,7 @@
                         pid, uid, packageName, reason);
                 mCb.onRewind(packageName, pid, uid);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in rewind.", e);
+                Slog.e(TAG, "Remote failure in rewind.", e);
             }
         }
 
@@ -1580,7 +1623,7 @@
                         pid, uid, packageName, reason);
                 mCb.onSeekTo(packageName, pid, uid, pos);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in seekTo.", e);
+                Slog.e(TAG, "Remote failure in seekTo.", e);
             }
         }
 
@@ -1591,7 +1634,7 @@
                         pid, uid, packageName, reason);
                 mCb.onRate(packageName, pid, uid, rating);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in rate.", e);
+                Slog.e(TAG, "Remote failure in rate.", e);
             }
         }
 
@@ -1602,7 +1645,7 @@
                         pid, uid, packageName, reason);
                 mCb.onSetPlaybackSpeed(packageName, pid, uid, speed);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in setPlaybackSpeed.", e);
+                Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e);
             }
         }
 
@@ -1619,7 +1662,7 @@
                     mCb.onAdjustVolume(packageName, pid, uid, direction);
                 }
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in adjustVolume.", e);
+                Slog.e(TAG, "Remote failure in adjustVolume.", e);
             }
         }
 
@@ -1630,7 +1673,7 @@
                         pid, uid, packageName, reason);
                 mCb.onSetVolumeTo(packageName, pid, uid, value);
             } catch (RemoteException e) {
-                Log.e(TAG, "Remote failure in setVolumeTo.", e);
+                Slog.e(TAG, "Remote failure in setVolumeTo.", e);
             }
         }
 
@@ -1673,8 +1716,10 @@
                         cb, packageName, Binder.getCallingUid(), () -> unregisterCallback(cb));
                     mControllerCallbackHolders.add(holder);
                     if (DEBUG) {
-                        Log.d(TAG, "registering controller callback " + cb + " from controller"
-                                + packageName);
+                        Slog.d(
+                                TAG,
+                                "registering controller callback " + cb
+                                        + " from controller" + packageName);
                     }
                     // Avoid callback leaks
                     try {
@@ -1683,7 +1728,7 @@
                         cb.asBinder().linkToDeath(holder.mDeathMonitor, 0);
                     } catch (RemoteException e) {
                         unregisterCallback(cb);
-                        Log.w(TAG, "registerCallback failed to linkToDeath", e);
+                        Slog.w(TAG, "registerCallback failed to linkToDeath", e);
                     }
                 }
             }
@@ -1698,12 +1743,12 @@
                         cb.asBinder().unlinkToDeath(
                           mControllerCallbackHolders.get(index).mDeathMonitor, 0);
                     } catch (NoSuchElementException e) {
-                        Log.w(TAG, "error unlinking to binder death", e);
+                        Slog.w(TAG, "error unlinking to binder death", e);
                     }
                     mControllerCallbackHolders.remove(index);
                 }
                 if (DEBUG) {
-                    Log.d(TAG, "unregistering callback " + cb.asBinder());
+                    Slog.d(TAG, "unregistering callback " + cb.asBinder());
                 }
             }
         }
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 78077a8..c8dba80 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -228,8 +228,8 @@
             return;
         }
 
-        MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();
-        if (TextUtils.equals(routeId, deviceRoute.getId())) {
+        MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
+        if (TextUtils.equals(routeId, selectedDeviceRoute.getId())) {
             mBluetoothRouteController.transferTo(null);
         } else {
             mBluetoothRouteController.transferTo(routeId);
@@ -278,11 +278,11 @@
                 return null;
             }
 
-            MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();
+            MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
 
             RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
                     SYSTEM_SESSION_ID, packageName).setSystemSession(true);
-            builder.addSelectedRoute(deviceRoute.getId());
+            builder.addSelectedRoute(selectedDeviceRoute.getId());
             for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) {
                 builder.addTransferableRoute(route.getId());
             }
@@ -314,7 +314,7 @@
         MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
 
         // We must have a device route in the provider info.
-        builder.addRoute(mDeviceRouteController.getDeviceRoute());
+        builder.addRoute(mDeviceRouteController.getSelectedRoute());
 
         for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) {
             builder.addRoute(route);
@@ -338,12 +338,12 @@
                     SYSTEM_SESSION_ID, "" /* clientPackageName */)
                     .setSystemSession(true);
 
-            MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();
-            MediaRoute2Info selectedRoute = deviceRoute;
+            MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
+            MediaRoute2Info selectedRoute = selectedDeviceRoute;
             MediaRoute2Info selectedBtRoute = mBluetoothRouteController.getSelectedRoute();
             if (selectedBtRoute != null) {
                 selectedRoute = selectedBtRoute;
-                builder.addTransferableRoute(deviceRoute.getId());
+                builder.addTransferableRoute(selectedDeviceRoute.getId());
             }
             mSelectedRouteId = selectedRoute.getId();
             mDefaultRoute =
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bae06347..c2b5964 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -184,6 +184,7 @@
 import android.companion.ICompanionDeviceManager;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.LoggingOnly;
 import android.content.AttributionSource;
 import android.content.BroadcastReceiver;
@@ -555,7 +556,7 @@
      * creation and activation of an implicit {@link android.app.AutomaticZenRule}.
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
     static final long MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES = 308670109L;
 
     private static final Duration POST_WAKE_LOCK_TIMEOUT = Duration.ofSeconds(30);
@@ -828,6 +829,22 @@
             }
         }
 
+        // Removes all notifications with the specified user & package.
+        public void removePackageNotifications(String pkg, @UserIdInt int userId) {
+            synchronized (mBufferLock) {
+                Iterator<Pair<StatusBarNotification, Integer>> bufferIter = descendingIterator();
+                while (bufferIter.hasNext()) {
+                    final Pair<StatusBarNotification, Integer> pair = bufferIter.next();
+                    if (pair.first != null
+                            && userId == pair.first.getNormalizedUserId()
+                            && pkg != null && pkg.equals(pair.first.getPackageName())
+                            && pair.first.getNotification() != null) {
+                        bufferIter.remove();
+                    }
+                }
+            }
+        }
+
         void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter) {
             synchronized (mBufferLock) {
                 Iterator<Pair<StatusBarNotification, Integer>> iter = descendingIterator();
@@ -1902,7 +1919,6 @@
                         unhideNotificationsForPackages(pkgList, uidList);
                     }
                 }
-
                 mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList);
             }
         }
@@ -4021,11 +4037,8 @@
                 Slog.e(TAG, "Failed to getApplicationInfo() in canUseFullScreenIntent()", e);
                 return false;
             }
-            final boolean showStickyHunIfDenied = mFlagResolver.isEnabled(
-                    SystemUiSystemPropertiesFlags.NotificationFlags
-                            .SHOW_STICKY_HUN_FOR_DENIED_FSI);
             return checkUseFullScreenIntentPermission(attributionSource, applicationInfo,
-                    showStickyHunIfDenied /* isAppOpPermission */, false /* forDataDelivery */);
+                    false /* forDataDelivery */);
         }
 
         @Override
@@ -4219,7 +4232,8 @@
             boolean previouslyExisted = mPreferencesHelper.deleteNotificationChannel(
                     pkg, callingUid, channelId, callingUid, isSystemOrSystemUi);
             if (previouslyExisted) {
-                // Remove from both recent notification archive and notification history
+                // Remove from both recent notification archive (recently dismissed notifications)
+                // and notification history
                 mArchive.removeChannelNotifications(pkg, callingUser, channelId);
                 mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId);
                 mListeners.notifyNotificationChannelChanged(pkg,
@@ -7274,28 +7288,12 @@
         notification.flags &= ~FLAG_FSI_REQUESTED_BUT_DENIED;
 
         if (notification.fullScreenIntent != null) {
-            final boolean forceDemoteFsiToStickyHun = mFlagResolver.isEnabled(
-                    SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE);
-            if (forceDemoteFsiToStickyHun) {
+            final AttributionSource attributionSource =
+                    new AttributionSource.Builder(notificationUid).setPackageName(pkg).build();
+            final boolean canUseFullScreenIntent = checkUseFullScreenIntentPermission(
+                    attributionSource, ai, true /* forDataDelivery */);
+            if (!canUseFullScreenIntent) {
                 makeStickyHun(notification, pkg, userId);
-            } else {
-                final AttributionSource attributionSource =
-                        new AttributionSource.Builder(notificationUid).setPackageName(pkg).build();
-                final boolean showStickyHunIfDenied = mFlagResolver.isEnabled(
-                        SystemUiSystemPropertiesFlags.NotificationFlags
-                                .SHOW_STICKY_HUN_FOR_DENIED_FSI);
-                final boolean canUseFullScreenIntent = checkUseFullScreenIntentPermission(
-                        attributionSource, ai, showStickyHunIfDenied /* isAppOpPermission */,
-                        true /* forDataDelivery */);
-                if (!canUseFullScreenIntent) {
-                    if (showStickyHunIfDenied) {
-                        makeStickyHun(notification, pkg, userId);
-                    } else {
-                        notification.fullScreenIntent = null;
-                        Slog.w(TAG, "Package " + pkg + ": Use of fullScreenIntent requires the"
-                                + "USE_FULL_SCREEN_INTENT permission");
-                    }
-                }
             }
         }
 
@@ -7402,27 +7400,20 @@
     }
 
     private boolean checkUseFullScreenIntentPermission(@NonNull AttributionSource attributionSource,
-            @NonNull ApplicationInfo applicationInfo, boolean isAppOpPermission,
+            @NonNull ApplicationInfo applicationInfo,
             boolean forDataDelivery) {
         if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q) {
             return true;
         }
-        if (isAppOpPermission) {
-            final int permissionResult;
-            if (forDataDelivery) {
-                permissionResult = mPermissionManager.checkPermissionForDataDelivery(
-                        permission.USE_FULL_SCREEN_INTENT, attributionSource, /* message= */ null);
-            } else {
-                permissionResult = mPermissionManager.checkPermissionForPreflight(
-                        permission.USE_FULL_SCREEN_INTENT, attributionSource);
-            }
-            return permissionResult == PermissionManager.PERMISSION_GRANTED;
+        final int permissionResult;
+        if (forDataDelivery) {
+            permissionResult = mPermissionManager.checkPermissionForDataDelivery(
+                    permission.USE_FULL_SCREEN_INTENT, attributionSource, /* message= */ null);
         } else {
-            final int permissionResult = getContext().checkPermission(
-                    permission.USE_FULL_SCREEN_INTENT, attributionSource.getPid(),
-                    attributionSource.getUid());
-            return permissionResult == PERMISSION_GRANTED;
+            permissionResult = mPermissionManager.checkPermissionForPreflight(
+                    permission.USE_FULL_SCREEN_INTENT, attributionSource);
         }
+        return permissionResult == PermissionManager.PERMISSION_GRANTED;
     }
 
     private void checkRemoteViews(String pkg, String tag, int id, Notification notification) {
@@ -9444,7 +9435,11 @@
             for (int i = 0; i < size; i++) {
                 final String pkg = pkgList[i];
                 final int uid = uidList[i];
-                mHistoryManager.onPackageRemoved(UserHandle.getUserId(uid), pkg);
+                final int userHandle = UserHandle.getUserId(uid);
+                // Removes this package's notifications from both recent notification archive
+                // (recently dismissed notifications) and notification history.
+                mArchive.removePackageNotifications(pkg, userHandle);
+                mHistoryManager.onPackageRemoved(userHandle, pkg);
             }
         }
         if (preferencesChanged) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index d2e980b..9a6ea2c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -530,16 +530,13 @@
             this.timeout_millis = p.r.getSbn().getNotification().getTimeoutAfter();
             this.is_non_dismissible = NotificationRecordLogger.isNonDismissible(p.r);
 
-            final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver()
-                    .isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI);
-
             final boolean hasFullScreenIntent =
                     p.r.getSbn().getNotification().fullScreenIntent != null;
 
             final boolean hasFsiRequestedButDeniedFlag =  (p.r.getSbn().getNotification().flags
                     & Notification.FLAG_FSI_REQUESTED_BUT_DENIED) != 0;
 
-            this.fsi_state = NotificationRecordLogger.getFsiState(isStickyHunFlagEnabled,
+            this.fsi_state = NotificationRecordLogger.getFsiState(
                     hasFullScreenIntent, hasFsiRequestedButDeniedFlag, eventType);
 
             this.is_locked = p.r.isLocked();
@@ -587,13 +584,10 @@
      * @return FrameworkStatsLog enum of the state of the full screen intent posted with this
      * notification.
      */
-    static int getFsiState(boolean isStickyHunFlagEnabled,
-                           boolean hasFullScreenIntent,
+    static int getFsiState(boolean hasFullScreenIntent,
                            boolean hasFsiRequestedButDeniedFlag,
                            NotificationReportedEvent eventType) {
-
-        if (!isStickyHunFlagEnabled
-                || eventType == NotificationReportedEvent.NOTIFICATION_UPDATED) {
+        if (eventType == NotificationReportedEvent.NOTIFICATION_UPDATED) {
             // Zeroes in protos take zero bandwidth, but non-zero numbers take bandwidth,
             // so we should log 0 when possible.
             return 0;
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 783e9bb..252664a 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -2143,10 +2143,7 @@
      * @return State of the full screen intent permission for this package.
      */
     @VisibleForTesting
-    int getFsiState(String pkg, int uid, boolean requestedFSIPermission, boolean isFlagEnabled) {
-        if (!isFlagEnabled) {
-            return 0;
-        }
+    int getFsiState(String pkg, int uid, boolean requestedFSIPermission) {
         if (!requestedFSIPermission) {
             return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
         }
@@ -2167,10 +2164,8 @@
      * the user.
      */
     @VisibleForTesting
-    boolean isFsiPermissionUserSet(String pkg, int uid, int fsiState, int currentPermissionFlags,
-                                   boolean isStickyHunFlagEnabled) {
-        if (!isStickyHunFlagEnabled
-                || fsiState == PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED) {
+    boolean isFsiPermissionUserSet(String pkg, int uid, int fsiState, int currentPermissionFlags) {
+        if (fsiState == PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED) {
             return false;
         }
         return (currentPermissionFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
@@ -2213,22 +2208,18 @@
                     pkgsWithPermissionsToHandle.remove(key);
                 }
 
-                final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver()
-                        .isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI);
-
                 final boolean requestedFSIPermission = mPermissionHelper.hasRequestedPermission(
                         android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg, r.uid);
 
-                final int fsiState = getFsiState(r.pkg, r.uid, requestedFSIPermission,
-                        isStickyHunFlagEnabled);
+                final int fsiState = getFsiState(r.pkg, r.uid, requestedFSIPermission);
 
                 final int currentPermissionFlags = mPm.getPermissionFlags(
                         android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg,
                         UserHandle.getUserHandleForUid(r.uid));
 
                 final boolean fsiIsUserSet =
-                        isFsiPermissionUserSet(r.pkg, r.uid, fsiState, currentPermissionFlags,
-                                isStickyHunFlagEnabled);
+                        isFsiPermissionUserSet(r.pkg, r.uid, fsiState,
+                                currentPermissionFlags);
 
                 events.add(FrameworkStatsLog.buildStatsEvent(
                         PACKAGE_NOTIFICATION_PREFERENCES,
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 79cd2a0..92d469c 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -259,6 +259,19 @@
      */
     boolean shouldFilterApplicationIncludingUninstalled(@Nullable PackageStateInternal ps,
             int callingUid, int userId);
+
+    /**
+     * Different from
+     * {@link #shouldFilterApplicationIncludingUninstalled(PackageStateInternal, int, int)}, the
+     * function returns {@code true} if:
+     * <ul>
+     * <li>The target package is not archived.
+     * <li>The package cannot be found in the device or has been uninstalled in the current user.
+     * </ul>
+     */
+    boolean shouldFilterApplicationIncludingUninstalledNotArchived(
+            @Nullable PackageStateInternal ps,
+            int callingUid, int userId);
     /**
      * Different from {@link #shouldFilterApplication(SharedUserSetting, int, int)}, the function
      * returns {@code true} if packages with the same shared user are all uninstalled in the current
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 11a6d1b..e5c4ccc 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2455,7 +2455,8 @@
      */
     public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
             int callingUid, @Nullable ComponentName component,
-            @PackageManager.ComponentType int componentType, int userId, boolean filterUninstall) {
+            @PackageManager.ComponentType int componentType, int userId, boolean filterUninstall,
+            boolean filterArchived) {
         if (Process.isSdkSandboxUid(callingUid)) {
             int clientAppUid = Process.getAppUidForSdkSandboxUid(callingUid);
             // SDK sandbox should be able to see it's client app
@@ -2469,14 +2470,20 @@
         }
         final String instantAppPkgName = getInstantAppPackageName(callingUid);
         final boolean callerIsInstantApp = instantAppPkgName != null;
+        final boolean packageArchivedForUser = ps != null && PackageArchiver.isArchived(
+                ps.getUserStateOrDefault(userId));
         // Don't treat hiddenUntilInstalled as an uninstalled state, phone app needs to access
         // these hidden application details to customize carrier apps. Also, allowing the system
         // caller accessing to application across users.
         if (ps == null
                 || (filterUninstall
-                        && !isSystemOrRootOrShell(callingUid)
-                        && !ps.isHiddenUntilInstalled()
-                        && !ps.getUserStateOrDefault(userId).isInstalled())) {
+                && !isSystemOrRootOrShell(callingUid)
+                && !ps.isHiddenUntilInstalled()
+                && !ps.getUserStateOrDefault(userId).isInstalled()
+                // Archived packages behave like uninstalled packages. So if filterUninstall is
+                // set to true, we dismiss filtering some uninstalled package only if it is
+                // archived and filterArchived is set as false.
+                && (!packageArchivedForUser || filterArchived))) {
             // If caller is instant app or sdk sandbox and ps is null, pretend the application
             // exists, but, needs to be filtered
             return (callerIsInstantApp || filterUninstall || Process.isSdkSandboxUid(callingUid));
@@ -2524,7 +2531,20 @@
     }
 
     /**
-     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean)
+     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
+     * boolean)
+     */
+    public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
+            int callingUid, @Nullable ComponentName component,
+            @PackageManager.ComponentType int componentType, int userId, boolean filterUninstall) {
+        return shouldFilterApplication(
+                ps, callingUid, component, componentType, userId, filterUninstall,
+                true /* filterArchived */);
+    }
+
+    /**
+     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
+     * boolean)
      */
     public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
             int callingUid, @Nullable ComponentName component,
@@ -2534,7 +2554,8 @@
     }
 
     /**
-     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean)
+     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
+     * boolean)
      */
     public final boolean shouldFilterApplication(
             @Nullable PackageStateInternal ps, int callingUid, int userId) {
@@ -2543,7 +2564,8 @@
     }
 
     /**
-     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean)
+     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
+     * boolean)
      */
     public final boolean shouldFilterApplication(@NonNull SharedUserSetting sus,
             int callingUid, int userId) {
@@ -2558,7 +2580,8 @@
     }
 
     /**
-     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean)
+     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
+     * boolean)
      */
     public final boolean shouldFilterApplicationIncludingUninstalled(
             @Nullable PackageStateInternal ps, int callingUid, int userId) {
@@ -2567,7 +2590,19 @@
     }
 
     /**
-     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean)
+     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
+     * boolean)
+     */
+    public final boolean shouldFilterApplicationIncludingUninstalledNotArchived(
+            @Nullable PackageStateInternal ps, int callingUid, int userId) {
+        return shouldFilterApplication(
+                ps, callingUid, null, TYPE_UNKNOWN, userId, true /* filterUninstall */,
+                false /* filterArchived */);
+    }
+
+    /**
+     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
+     * boolean)
      */
     public final boolean shouldFilterApplicationIncludingUninstalled(
             @NonNull SharedUserSetting sus, int callingUid, int userId) {
@@ -5015,7 +5050,7 @@
         String installerPackageName = installSource.mInstallerPackageName;
         if (installerPackageName != null) {
             final PackageStateInternal ps = mSettings.getPackage(installerPackageName);
-            if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid,
+            if (ps == null || shouldFilterApplicationIncludingUninstalledNotArchived(ps, callingUid,
                     UserHandle.getUserId(callingUid))) {
                 installerPackageName = null;
             }
@@ -5033,7 +5068,8 @@
             return InstallSource.EMPTY;
         }
 
-        if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
+        if (ps == null || shouldFilterApplicationIncludingUninstalledNotArchived(ps, callingUid,
+                userId)) {
             return null;
         }
 
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 65c6329..3b3d79e 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -440,7 +440,7 @@
         if (outInfo != null) {
             // Remember which users are affected, before the installed states are modified
             outInfo.mRemovedUsers = (systemApp || userId == UserHandle.USER_ALL)
-                    ? ps.queryInstalledUsers(allUserHandles, /* installed= */true)
+                    ? ps.queryUsersInstalledOrHasData(allUserHandles)
                     : new int[]{userId};
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d38c0a0..47d1df5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3586,21 +3586,30 @@
                 params.setDontKillApp(false);
             }
 
+            boolean existingSplitReplacedOrRemoved = false;
             // Inherit splits if not overridden.
             if (!ArrayUtils.isEmpty(existing.getSplitNames())) {
                 for (int i = 0; i < existing.getSplitNames().length; i++) {
                     final String splitName = existing.getSplitNames()[i];
                     final File splitFile = new File(existing.getSplitApkPaths()[i]);
                     final boolean splitRemoved = removeSplitList.contains(splitName);
-                    if (!stagedSplits.contains(splitName) && !splitRemoved) {
+                    final boolean splitReplaced = stagedSplits.contains(splitName);
+                    if (!splitReplaced && !splitRemoved) {
                         inheritFileLocked(splitFile);
                         // Collect the requiredSplitTypes and staged splitTypes from splits
                         CollectionUtils.addAll(requiredSplitTypes,
                                 existing.getRequiredSplitTypes()[i]);
                         CollectionUtils.addAll(stagedSplitTypes, existing.getSplitTypes()[i]);
+                    } else {
+                        existingSplitReplacedOrRemoved = true;
                     }
                 }
             }
+            if (existingSplitReplacedOrRemoved
+                    && (params.installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
+                // Some splits are being replaced or removed. Make sure the app is restarted.
+                params.setDontKillApp(false);
+            }
 
             // Inherit compiled oat directory.
             final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 72090f2..b50d0a0 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -779,6 +779,10 @@
         return readUserState(userId).isInstalled();
     }
 
+    boolean isArchived(int userId) {
+        return PackageArchiver.isArchived(readUserState(userId));
+    }
+
     int getInstallReason(int userId) {
         return readUserState(userId).getInstallReason();
     }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4e14c90..8556317 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1592,6 +1592,19 @@
      */
     private void showConfirmCredentialToDisableQuietMode(
             @UserIdInt int userId, @Nullable IntentSender target) {
+        if (android.app.admin.flags.Flags.quietModeCredentialBugFix()) {
+            // TODO (b/308121702) It may be brittle to rely on user states to check profile state
+            int state;
+            synchronized (mUserStates) {
+                state = mUserStates.get(userId, UserState.STATE_NONE);
+            }
+            if (state != UserState.STATE_NONE) {
+                Slog.i(LOG_TAG,
+                        "showConfirmCredentialToDisableQuietMode() called too early, user " + userId
+                                + " is still alive.");
+                return;
+            }
+        }
         // otherwise, we show a profile challenge to trigger decryption of the user
         final KeyguardManager km = (KeyguardManager) mContext.getSystemService(
                 Context.KEYGUARD_SERVICE);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 17499bb7..940feb5 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -24,12 +24,14 @@
 import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_PASSTHROUGH;
 import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_UNSUPPORTED;
 import static android.hardware.graphics.common.Hdr.DOLBY_VISION;
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkStats.METERED_YES;
 import static android.net.NetworkTemplate.MATCH_ETHERNET;
 import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.MATCH_PROXY;
 import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.OEM_MANAGED_ALL;
 import static android.net.NetworkTemplate.OEM_MANAGED_PAID;
@@ -488,6 +490,7 @@
                     case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG:
                     case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
                     case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
+                    case FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG:
                     case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED:
                     case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER:
                     case FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER:
@@ -973,21 +976,33 @@
         if (DEBUG) {
             Slog.d(TAG, "Registering NetworkStats pullers with statsd");
         }
+
+        boolean canQueryTypeProxy = canQueryNetworkStatsForTypeProxy();
+
         // Initialize NetworkStats baselines.
-        mNetworkStatsBaselines.addAll(
-                collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER));
-        mNetworkStatsBaselines.addAll(
-                collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG));
-        mNetworkStatsBaselines.addAll(
-                collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.MOBILE_BYTES_TRANSFER));
-        mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
-                FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG));
-        mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
-                FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED));
-        mNetworkStatsBaselines.addAll(
-                collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER));
-        mNetworkStatsBaselines.addAll(
-                collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER));
+        synchronized (mDataBytesTransferLock) {
+            mNetworkStatsBaselines.addAll(
+                    collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER));
+            mNetworkStatsBaselines.addAll(
+                    collectNetworkStatsSnapshotForAtom(
+                            FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG));
+            mNetworkStatsBaselines.addAll(
+                    collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.MOBILE_BYTES_TRANSFER));
+            mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
+                    FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG));
+            mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
+                    FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED));
+            mNetworkStatsBaselines.addAll(
+                    collectNetworkStatsSnapshotForAtom(
+                            FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER));
+            mNetworkStatsBaselines.addAll(
+                    collectNetworkStatsSnapshotForAtom(
+                            FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER));
+            if (canQueryTypeProxy) {
+                mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
+                        FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG));
+            }
+        }
 
         // Listen to subscription changes to record historical subscriptions that activated before
         // pulling, this is used by {@code DATA_USAGE_BYTES_TRANSFER}.
@@ -1001,6 +1016,9 @@
         registerBytesTransferByTagAndMetered();
         registerDataUsageBytesTransfer();
         registerOemManagedBytesTransfer();
+        if (canQueryTypeProxy) {
+            registerProxyBytesTransferBackground();
+        }
     }
 
     private void initAndRegisterDeferredPullers() {
@@ -1171,6 +1189,18 @@
                 }
                 break;
             }
+            case FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG: {
+                final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(
+                        new NetworkTemplate.Builder(MATCH_PROXY).build(),  /*includeTags=*/true);
+                if (stats != null) {
+                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
+                            new int[]{TRANSPORT_BLUETOOTH},
+                            /*slicedByFgbg=*/true, /*slicedByTag=*/false,
+                            /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                            /*subInfo=*/null, OEM_MANAGED_ALL, /*isTypeProxy=*/true));
+                }
+                break;
+            }
             case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: {
                 final NetworkStats wifiStats = getUidNetworkStatsSnapshotForTemplate(
                         new NetworkTemplate.Builder(MATCH_WIFI).build(), /*includeTags=*/true);
@@ -1183,7 +1213,7 @@
                             new int[]{TRANSPORT_WIFI, TRANSPORT_CELLULAR},
                             /*slicedByFgbg=*/false, /*slicedByTag=*/true,
                             /*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                            /*subInfo=*/null, OEM_MANAGED_ALL));
+                            /*subInfo=*/null, OEM_MANAGED_ALL, /*isTypeProxy=*/false));
                 }
                 break;
             }
@@ -1225,7 +1255,7 @@
             final NetworkStatsExt diff = new NetworkStatsExt(
                     removeEmptyEntries(item.stats.subtract(baseline.stats)), item.transports,
                     item.slicedByFgbg, item.slicedByTag, item.slicedByMetered, item.ratType,
-                    item.subInfo, item.oemManaged);
+                    item.subInfo, item.oemManaged, item.isTypeProxy);
 
             // If no diff, skip.
             if (!diff.stats.iterator().hasNext()) continue;
@@ -1363,7 +1393,7 @@
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
                             new int[]{transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/false,
                             /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                            /*subInfo=*/null, oemManaged));
+                            /*subInfo=*/null, oemManaged, /*isTypeProxy=*/false));
                 }
             }
         }
@@ -1392,6 +1422,21 @@
     }
 
     /**
+     * Check if it is possible to query NetworkStats for TYPE_PROXY. This should only be possible
+     * if the build includes r.android.com/2828315
+     * @return true if querying for TYPE_PROXY is allowed
+     */
+    private static boolean canQueryNetworkStatsForTypeProxy() {
+        try {
+            new NetworkTemplate.Builder(MATCH_PROXY).build();
+            return true;
+        } catch (IllegalArgumentException e) {
+            Slog.w(TAG, "Querying network stats for TYPE_PROXY is not allowed");
+            return false;
+        }
+    }
+
+    /**
      * Create a snapshot of NetworkStats since boot for the given template, but add 1 bucket
      * duration before boot as a buffer to ensure at least one full bucket will be included.
      * Note that this should be only used to calculate diff since the snapshot might contains
@@ -1450,7 +1495,7 @@
                 ret.add(new NetworkStatsExt(sliceNetworkStatsByFgbg(stats),
                         new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true,
                         /*slicedByTag=*/false, /*slicedByMetered=*/false, ratType, subInfo,
-                        OEM_MANAGED_ALL));
+                        OEM_MANAGED_ALL, /*isTypeProxy=*/false));
             }
         }
         return ret;
@@ -1600,6 +1645,19 @@
         );
     }
 
+    private void registerProxyBytesTransferBackground() {
+        int tagId = FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG;
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
+                .setAdditiveFields(new int[]{3, 4, 5, 6})
+                .build();
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                metadata,
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl
+        );
+    }
+
     private void registerBytesTransferByTagAndMetered() {
         int tagId = FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
diff --git a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java
index 7dbba0d..512f0bf 100644
--- a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java
+++ b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java
@@ -42,15 +42,17 @@
     public final int oemManaged;
     @Nullable
     public final SubInfo subInfo;
+    public final boolean isTypeProxy; // True if matching ConnectivityManager#TYPE_PROXY
 
     public NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg) {
         this(stats, transports, slicedByFgbg, /*slicedByTag=*/false, /*slicedByMetered=*/false,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, /*subInfo=*/null, OEM_MANAGED_ALL);
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, /*subInfo=*/null,
+                OEM_MANAGED_ALL, /*isTypeProxy=*/false);
     }
 
     public NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg,
             boolean slicedByTag, boolean slicedByMetered, int ratType,
-            @Nullable SubInfo subInfo, int oemManaged) {
+            @Nullable SubInfo subInfo, int oemManaged, boolean isTypeProxy) {
         this.stats = stats;
 
         // Sort transports array so that we can test for equality without considering order.
@@ -63,6 +65,7 @@
         this.ratType = ratType;
         this.subInfo = subInfo;
         this.oemManaged = oemManaged;
+        this.isTypeProxy = isTypeProxy;
     }
 
     /**
@@ -72,6 +75,6 @@
         return Arrays.equals(transports, other.transports) && slicedByFgbg == other.slicedByFgbg
                 && slicedByTag == other.slicedByTag && slicedByMetered == other.slicedByMetered
                 && ratType == other.ratType && Objects.equals(subInfo, other.subInfo)
-                && oemManaged == other.oemManaged;
+                && oemManaged == other.oemManaged && isTypeProxy == other.isTypeProxy;
     }
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index ee46ce1..b3672ec 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.webkit;
 
+import static android.webkit.Flags.updateServiceV2;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -51,7 +53,7 @@
     private static final String TAG = "WebViewUpdateService";
 
     private BroadcastReceiver mWebViewUpdatedReceiver;
-    private WebViewUpdateServiceImpl mImpl;
+    private WebViewUpdateServiceInterface mImpl;
 
     static final int PACKAGE_CHANGED = 0;
     static final int PACKAGE_ADDED = 1;
@@ -60,7 +62,11 @@
 
     public WebViewUpdateService(Context context) {
         super(context);
-        mImpl = new WebViewUpdateServiceImpl(context, SystemImpl.getInstance());
+        if (updateServiceV2()) {
+            mImpl = new WebViewUpdateServiceImpl2(context, SystemImpl.getInstance());
+        } else {
+            mImpl = new WebViewUpdateServiceImpl(context, SystemImpl.getInstance());
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 43d62aa..cfdef14 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -63,7 +63,7 @@
  *
  * @hide
  */
-class WebViewUpdateServiceImpl {
+class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface {
     private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName();
 
     private static class WebViewPackageMissingException extends Exception {
@@ -112,7 +112,8 @@
         mSystemInterface = systemInterface;
     }
 
-    void packageStateChanged(String packageName, int changedState, int userId) {
+    @Override
+    public void packageStateChanged(String packageName, int changedState, int userId) {
         // We don't early out here in different cases where we could potentially early-out (e.g. if
         // we receive PACKAGE_CHANGED for another user than the system user) since that would
         // complicate this logic further and open up for more edge cases.
@@ -163,7 +164,8 @@
         }
     }
 
-    void prepareWebViewInSystemServer() {
+    @Override
+    public void prepareWebViewInSystemServer() {
         mSystemInterface.notifyZygote(isMultiProcessEnabled());
         try {
             synchronized (mLock) {
@@ -210,7 +212,8 @@
         mSystemInterface.ensureZygoteStarted();
     }
 
-    void handleNewUser(int userId) {
+    @Override
+    public void handleNewUser(int userId) {
         // The system user is always started at boot, and by that point we have already run one
         // round of the package-changing logic (through prepareWebViewInSystemServer()), so early
         // out here.
@@ -218,7 +221,8 @@
         handleUserChange();
     }
 
-    void handleUserRemoved(int userId) {
+    @Override
+    public void handleUserRemoved(int userId) {
         handleUserChange();
     }
 
@@ -232,14 +236,16 @@
         updateCurrentWebViewPackage(null);
     }
 
-    void notifyRelroCreationCompleted() {
+    @Override
+    public void notifyRelroCreationCompleted() {
         synchronized (mLock) {
             mNumRelroCreationsFinished++;
             checkIfRelrosDoneLocked();
         }
     }
 
-    WebViewProviderResponse waitForAndGetProvider() {
+    @Override
+    public WebViewProviderResponse waitForAndGetProvider() {
         PackageInfo webViewPackage = null;
         final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
         boolean webViewReady = false;
@@ -284,7 +290,8 @@
      * replacing that provider it will not be in use directly, but will be used when the relros
      * or the replacement are done).
      */
-    String changeProviderAndSetting(String newProviderName) {
+    @Override
+    public String changeProviderAndSetting(String newProviderName) {
         PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
         if (newPackage == null) return "";
         return newPackage.packageName;
@@ -367,7 +374,8 @@
     /**
      * Fetch only the currently valid WebView packages.
      **/
-    WebViewProviderInfo[] getValidWebViewPackages() {
+    @Override
+    public WebViewProviderInfo[] getValidWebViewPackages() {
         ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
         WebViewProviderInfo[] providers =
             new WebViewProviderInfo[providersAndPackageInfos.length];
@@ -464,11 +472,13 @@
         return true;
     }
 
-    WebViewProviderInfo[] getWebViewPackages() {
+    @Override
+    public WebViewProviderInfo[] getWebViewPackages() {
         return mSystemInterface.getWebViewPackages();
     }
 
-    PackageInfo getCurrentWebViewPackage() {
+    @Override
+    public PackageInfo getCurrentWebViewPackage() {
         synchronized (mLock) {
             return mCurrentWebViewPackage;
         }
@@ -620,7 +630,8 @@
         return null;
     }
 
-    boolean isMultiProcessEnabled() {
+    @Override
+    public boolean isMultiProcessEnabled() {
         int settingValue = mSystemInterface.getMultiProcessSetting(mContext);
         if (mSystemInterface.isMultiProcessDefaultEnabled()) {
             // Multiprocess should be enabled unless the user has turned it off manually.
@@ -631,7 +642,8 @@
         }
     }
 
-    void enableMultiProcess(boolean enable) {
+    @Override
+    public void enableMultiProcess(boolean enable) {
         PackageInfo current = getCurrentWebViewPackage();
         mSystemInterface.setMultiProcessSetting(mContext,
                 enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE);
@@ -644,7 +656,8 @@
     /**
      * Dump the state of this Service.
      */
-    void dumpState(PrintWriter pw) {
+    @Override
+    public void dumpState(PrintWriter pw) {
         pw.println("Current WebView Update Service state");
         pw.println(String.format("  Multiprocess enabled: %b", isMultiProcessEnabled()));
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
new file mode 100644
index 0000000..e618c7e
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -0,0 +1,747 @@
+/*
+ * Copyright (C) 2016 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.webkit;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
+import android.os.AsyncTask;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.webkit.UserPackage;
+import android.webkit.WebViewFactory;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewProviderResponse;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of the WebViewUpdateService.
+ * This class doesn't depend on the android system like the actual Service does and can be used
+ * directly by tests (as long as they implement a SystemInterface).
+ *
+ * This class keeps track of and prepares the current WebView implementation, and needs to keep
+ * track of a couple of different things such as what package is used as WebView implementation.
+ *
+ * The package-visible methods in this class are accessed from WebViewUpdateService either on the UI
+ * thread or on one of multiple Binder threads. The WebView preparation code shares state between
+ * threads meaning that code that chooses a new WebView implementation or checks which
+ * implementation is being used needs to hold a lock.
+ *
+ * The WebViewUpdateService can be accessed in a couple of different ways.
+ * 1. It is started from the SystemServer at boot - at that point we just initiate some state such
+ * as the WebView preparation class.
+ * 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot
+ * and the WebViewUpdateService should not have been accessed before this call. In this call we
+ * choose WebView implementation for the first time.
+ * 3. The update service listens for Intents related to package installs and removals. These intents
+ * are received and processed on the UI thread. Each intent can result in changing WebView
+ * implementation.
+ * 4. The update service can be reached through Binder calls which are handled on specific binder
+ * threads. These calls can be made from any process. Generally they are used for changing WebView
+ * implementation (from Settings), getting information about the current WebView implementation (for
+ * loading WebView into an app process), or notifying the service about Relro creation being
+ * completed.
+ *
+ * @hide
+ */
+class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
+    private static final String TAG = WebViewUpdateServiceImpl2.class.getSimpleName();
+
+    private static class WebViewPackageMissingException extends Exception {
+        WebViewPackageMissingException(String message) {
+            super(message);
+        }
+
+        WebViewPackageMissingException(Exception e) {
+            super(e);
+        }
+    }
+
+    private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
+    private static final long NS_PER_MS = 1000000;
+
+    private static final int VALIDITY_OK = 0;
+    private static final int VALIDITY_INCORRECT_SDK_VERSION = 1;
+    private static final int VALIDITY_INCORRECT_VERSION_CODE = 2;
+    private static final int VALIDITY_INCORRECT_SIGNATURE = 3;
+    private static final int VALIDITY_NO_LIBRARY_FLAG = 4;
+
+    private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
+    private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
+
+    private final SystemInterface mSystemInterface;
+    private final Context mContext;
+
+    private long mMinimumVersionCode = -1;
+
+    // Keeps track of the number of running relro creations
+    private int mNumRelroCreationsStarted = 0;
+    private int mNumRelroCreationsFinished = 0;
+    // Implies that we need to rerun relro creation because we are using an out-of-date package
+    private boolean mWebViewPackageDirty = false;
+    private boolean mAnyWebViewInstalled = false;
+
+    private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
+
+    // The WebView package currently in use (or the one we are preparing).
+    private PackageInfo mCurrentWebViewPackage = null;
+
+    private final Object mLock = new Object();
+
+    WebViewUpdateServiceImpl2(Context context, SystemInterface systemInterface) {
+        mContext = context;
+        mSystemInterface = systemInterface;
+    }
+
+    @Override
+    public void packageStateChanged(String packageName, int changedState, int userId) {
+        // We don't early out here in different cases where we could potentially early-out (e.g. if
+        // we receive PACKAGE_CHANGED for another user than the system user) since that would
+        // complicate this logic further and open up for more edge cases.
+        for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+            String webviewPackage = provider.packageName;
+
+            if (webviewPackage.equals(packageName)) {
+                boolean updateWebView = false;
+                boolean removedOrChangedOldPackage = false;
+                String oldProviderName = null;
+                PackageInfo newPackage = null;
+                synchronized (mLock) {
+                    try {
+                        newPackage = findPreferredWebViewPackage();
+                        if (mCurrentWebViewPackage != null) {
+                            oldProviderName = mCurrentWebViewPackage.packageName;
+                        }
+                        // Only trigger update actions if the updated package is the one
+                        // that will be used, or the one that was in use before the
+                        // update, or if we haven't seen a valid WebView package before.
+                        updateWebView =
+                            provider.packageName.equals(newPackage.packageName)
+                            || provider.packageName.equals(oldProviderName)
+                            || mCurrentWebViewPackage == null;
+                        // We removed the old package if we received an intent to remove
+                        // or replace the old package.
+                        removedOrChangedOldPackage =
+                            provider.packageName.equals(oldProviderName);
+                        if (updateWebView) {
+                            onWebViewProviderChanged(newPackage);
+                        }
+                    } catch (WebViewPackageMissingException e) {
+                        mCurrentWebViewPackage = null;
+                        Slog.e(TAG, "Could not find valid WebView package to create relro with "
+                                + e);
+                    }
+                }
+                if (updateWebView && !removedOrChangedOldPackage
+                        && oldProviderName != null) {
+                    // If the provider change is the result of adding or replacing a
+                    // package that was not the previous provider then we must kill
+                    // packages dependent on the old package ourselves. The framework
+                    // only kills dependents of packages that are being removed.
+                    mSystemInterface.killPackageDependents(oldProviderName);
+                }
+                return;
+            }
+        }
+    }
+
+    @Override
+    public void prepareWebViewInSystemServer() {
+        mSystemInterface.notifyZygote(isMultiProcessEnabled());
+        try {
+            synchronized (mLock) {
+                mCurrentWebViewPackage = findPreferredWebViewPackage();
+                String userSetting = mSystemInterface.getUserChosenWebViewProvider(mContext);
+                if (userSetting != null
+                        && !userSetting.equals(mCurrentWebViewPackage.packageName)) {
+                    // Don't persist the user-chosen setting across boots if the package being
+                    // chosen is not used (could be disabled or uninstalled) so that the user won't
+                    // be surprised by the device switching to using a certain webview package,
+                    // that was uninstalled/disabled a long time ago, if it is installed/enabled
+                    // again.
+                    mSystemInterface.updateUserSetting(mContext,
+                            mCurrentWebViewPackage.packageName);
+                }
+                onWebViewProviderChanged(mCurrentWebViewPackage);
+            }
+        } catch (Throwable t) {
+            // Log and discard errors at this stage as we must not crash the system server.
+            Slog.e(TAG, "error preparing webview provider from system server", t);
+        }
+
+        if (getCurrentWebViewPackage() == null) {
+            // We didn't find a valid WebView implementation. Try explicitly re-enabling the
+            // fallback package for all users in case it was disabled, even if we already did the
+            // one-time migration before. If this actually changes the state, we will see the
+            // PackageManager broadcast shortly and try again.
+            WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
+            WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
+            if (fallbackProvider != null) {
+                Slog.w(TAG, "No valid provider, trying to enable " + fallbackProvider.packageName);
+                mSystemInterface.enablePackageForAllUsers(mContext, fallbackProvider.packageName,
+                                                          true);
+            } else {
+                Slog.e(TAG, "No valid provider and no fallback available.");
+            }
+        }
+    }
+
+    private void startZygoteWhenReady() {
+        // Wait on a background thread for RELRO creation to be done. We ignore the return value
+        // because even if RELRO creation failed we still want to start the zygote.
+        waitForAndGetProvider();
+        mSystemInterface.ensureZygoteStarted();
+    }
+
+    @Override
+    public void handleNewUser(int userId) {
+        // The system user is always started at boot, and by that point we have already run one
+        // round of the package-changing logic (through prepareWebViewInSystemServer()), so early
+        // out here.
+        if (userId == UserHandle.USER_SYSTEM) return;
+        handleUserChange();
+    }
+
+    @Override
+    public void handleUserRemoved(int userId) {
+        handleUserChange();
+    }
+
+    /**
+     * Called when a user was added or removed to ensure WebView preparation is triggered.
+     * This has to be done since the WebView package we use depends on the enabled-state
+     * of packages for all users (so adding or removing a user might cause us to change package).
+     */
+    private void handleUserChange() {
+        // Potentially trigger package-changing logic.
+        updateCurrentWebViewPackage(null);
+    }
+
+    @Override
+    public void notifyRelroCreationCompleted() {
+        synchronized (mLock) {
+            mNumRelroCreationsFinished++;
+            checkIfRelrosDoneLocked();
+        }
+    }
+
+    @Override
+    public WebViewProviderResponse waitForAndGetProvider() {
+        PackageInfo webViewPackage = null;
+        final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
+        boolean webViewReady = false;
+        int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
+        synchronized (mLock) {
+            webViewReady = webViewIsReadyLocked();
+            while (!webViewReady) {
+                final long timeNowMs = System.nanoTime() / NS_PER_MS;
+                if (timeNowMs >= timeoutTimeMs) break;
+                try {
+                    mLock.wait(timeoutTimeMs - timeNowMs);
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+                webViewReady = webViewIsReadyLocked();
+            }
+            // Make sure we return the provider that was used to create the relro file
+            webViewPackage = mCurrentWebViewPackage;
+            if (webViewReady) {
+                // success
+            } else if (!mAnyWebViewInstalled) {
+                webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+            } else {
+                // Either the current relro creation  isn't done yet, or the new relro creatioin
+                // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
+                webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
+                String timeoutError = "Timed out waiting for relro creation, relros started "
+                        + mNumRelroCreationsStarted
+                        + " relros finished " + mNumRelroCreationsFinished
+                        + " package dirty? " + mWebViewPackageDirty;
+                Slog.e(TAG, timeoutError);
+                Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, timeoutError);
+            }
+        }
+        if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
+        return new WebViewProviderResponse(webViewPackage, webViewStatus);
+    }
+
+    /**
+     * Change WebView provider and provider setting and kill packages using the old provider.
+     * Return the new provider (in case we are in the middle of creating relro files, or
+     * replacing that provider it will not be in use directly, but will be used when the relros
+     * or the replacement are done).
+     */
+    @Override
+    public String changeProviderAndSetting(String newProviderName) {
+        PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
+        if (newPackage == null) return "";
+        return newPackage.packageName;
+    }
+
+    /**
+     * Update the current WebView package.
+     * @param newProviderName the package to switch to, null if no package has been explicitly
+     * chosen.
+     */
+    private PackageInfo updateCurrentWebViewPackage(@Nullable String newProviderName) {
+        PackageInfo oldPackage = null;
+        PackageInfo newPackage = null;
+        boolean providerChanged = false;
+        synchronized (mLock) {
+            oldPackage = mCurrentWebViewPackage;
+
+            if (newProviderName != null) {
+                mSystemInterface.updateUserSetting(mContext, newProviderName);
+            }
+
+            try {
+                newPackage = findPreferredWebViewPackage();
+                providerChanged = (oldPackage == null)
+                        || !newPackage.packageName.equals(oldPackage.packageName);
+            } catch (WebViewPackageMissingException e) {
+                // If updated the Setting but don't have an installed WebView package, the
+                // Setting will be used when a package is available.
+                mCurrentWebViewPackage = null;
+                Slog.e(TAG, "Couldn't find WebView package to use " + e);
+                return null;
+            }
+            // Perform the provider change if we chose a new provider
+            if (providerChanged) {
+                onWebViewProviderChanged(newPackage);
+            }
+        }
+        // Kill apps using the old provider only if we changed provider
+        if (providerChanged && oldPackage != null) {
+            mSystemInterface.killPackageDependents(oldPackage.packageName);
+        }
+        // Return the new provider, this is not necessarily the one we were asked to switch to,
+        // but the persistent setting will now be pointing to the provider we were asked to
+        // switch to anyway.
+        return newPackage;
+    }
+
+    /**
+     * This is called when we change WebView provider, either when the current provider is
+     * updated or a new provider is chosen / takes precedence.
+     */
+    private void onWebViewProviderChanged(PackageInfo newPackage) {
+        synchronized (mLock) {
+            mAnyWebViewInstalled = true;
+            if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+                mCurrentWebViewPackage = newPackage;
+
+                // The relro creations might 'finish' (not start at all) before
+                // WebViewFactory.onWebViewProviderChanged which means we might not know the
+                // number of started creations before they finish.
+                mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
+                mNumRelroCreationsFinished = 0;
+                mNumRelroCreationsStarted =
+                    mSystemInterface.onWebViewProviderChanged(newPackage);
+                // If the relro creations finish before we know the number of started creations
+                // we will have to do any cleanup/notifying here.
+                checkIfRelrosDoneLocked();
+            } else {
+                mWebViewPackageDirty = true;
+            }
+        }
+
+        // Once we've notified the system that the provider has changed and started RELRO creation,
+        // try to restart the zygote so that it will be ready when apps use it.
+        if (isMultiProcessEnabled()) {
+            AsyncTask.THREAD_POOL_EXECUTOR.execute(this::startZygoteWhenReady);
+        }
+    }
+
+    /**
+     * Fetch only the currently valid WebView packages.
+     **/
+    @Override
+    public WebViewProviderInfo[] getValidWebViewPackages() {
+        ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
+        WebViewProviderInfo[] providers =
+                new WebViewProviderInfo[providersAndPackageInfos.length];
+        for (int n = 0; n < providersAndPackageInfos.length; n++) {
+            providers[n] = providersAndPackageInfos[n].provider;
+        }
+        return providers;
+    }
+
+    private static class ProviderAndPackageInfo {
+        public final WebViewProviderInfo provider;
+        public final PackageInfo packageInfo;
+
+        ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
+            this.provider = provider;
+            this.packageInfo = packageInfo;
+        }
+    }
+
+    private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
+        WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
+        List<ProviderAndPackageInfo> providers = new ArrayList<>();
+        for (int n = 0; n < allProviders.length; n++) {
+            try {
+                PackageInfo packageInfo =
+                        mSystemInterface.getPackageInfoForProvider(allProviders[n]);
+                if (validityResult(allProviders[n], packageInfo) == VALIDITY_OK) {
+                    providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
+                }
+            } catch (NameNotFoundException e) {
+                // Don't add non-existent packages
+            }
+        }
+        return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
+    }
+
+    /**
+     * Returns either the package info of the WebView provider determined in the following way:
+     * If the user has chosen a provider then use that if it is valid,
+     * otherwise use the first package in the webview priority list that is valid.
+     *
+     */
+    private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
+        ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
+
+        String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
+
+        // If the user has chosen provider, use that (if it's installed and enabled for all
+        // users).
+        for (ProviderAndPackageInfo providerAndPackage : providers) {
+            if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
+                // userPackages can contain null objects.
+                List<UserPackage> userPackages =
+                        mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
+                                providerAndPackage.provider);
+                if (isInstalledAndEnabledForAllUsers(userPackages)) {
+                    return providerAndPackage.packageInfo;
+                }
+            }
+        }
+
+        // User did not choose, or the choice failed; use the most stable provider that is
+        // installed and enabled for all users, and available by default (not through
+        // user choice).
+        for (ProviderAndPackageInfo providerAndPackage : providers) {
+            if (providerAndPackage.provider.availableByDefault) {
+                // userPackages can contain null objects.
+                List<UserPackage> userPackages =
+                        mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
+                                providerAndPackage.provider);
+                if (isInstalledAndEnabledForAllUsers(userPackages)) {
+                    return providerAndPackage.packageInfo;
+                }
+            }
+        }
+
+        // This should never happen during normal operation (only with modified system images).
+        mAnyWebViewInstalled = false;
+        throw new WebViewPackageMissingException("Could not find a loadable WebView package");
+    }
+
+    /**
+     * Return true iff {@param packageInfos} point to only installed and enabled packages.
+     * The given packages {@param packageInfos} should all be pointing to the same package, but each
+     * PackageInfo representing a different user's package.
+     */
+    private static boolean isInstalledAndEnabledForAllUsers(
+            List<UserPackage> userPackages) {
+        for (UserPackage userPackage : userPackages) {
+            if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public WebViewProviderInfo[] getWebViewPackages() {
+        return mSystemInterface.getWebViewPackages();
+    }
+
+    @Override
+    public PackageInfo getCurrentWebViewPackage() {
+        synchronized (mLock) {
+            return mCurrentWebViewPackage;
+        }
+    }
+
+    /**
+     * Returns whether WebView is ready and is not going to go through its preparation phase
+     * again directly.
+     */
+    private boolean webViewIsReadyLocked() {
+        return !mWebViewPackageDirty
+            && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
+            // The current package might be replaced though we haven't received an intent
+            // declaring this yet, the following flag makes anyone loading WebView to wait in
+            // this case.
+            && mAnyWebViewInstalled;
+    }
+
+    private void checkIfRelrosDoneLocked() {
+        if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+            if (mWebViewPackageDirty) {
+                mWebViewPackageDirty = false;
+                // If we have changed provider since we started the relro creation we need to
+                // redo the whole process using the new package instead.
+                try {
+                    PackageInfo newPackage = findPreferredWebViewPackage();
+                    onWebViewProviderChanged(newPackage);
+                } catch (WebViewPackageMissingException e) {
+                    mCurrentWebViewPackage = null;
+                    // If we can't find any valid WebView package we are now in a state where
+                    // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
+                    // should simply wait until we receive an intent declaring a new package was
+                    // installed.
+                }
+            } else {
+                mLock.notifyAll();
+            }
+        }
+    }
+
+    private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
+        // Ensure the provider targets this framework release (or a later one).
+        if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
+            return VALIDITY_INCORRECT_SDK_VERSION;
+        }
+        if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
+                && !mSystemInterface.systemIsDebuggable()) {
+            // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
+            // minimum version code. This check is only enforced for user builds.
+            return VALIDITY_INCORRECT_VERSION_CODE;
+        }
+        if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) {
+            return VALIDITY_INCORRECT_SIGNATURE;
+        }
+        if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) {
+            return VALIDITY_NO_LIBRARY_FLAG;
+        }
+        return VALIDITY_OK;
+    }
+
+    /**
+     * Both versionCodes should be from a WebView provider package implemented by Chromium.
+     * VersionCodes from other kinds of packages won't make any sense in this method.
+     *
+     * An introduction to Chromium versionCode scheme:
+     * "BBBBPPPXX"
+     * BBBB: 4 digit branch number. It monotonically increases over time.
+     * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits
+     * may change their meaning in the future.
+     * XX: Digits to differentiate different APK builds of the same source version.
+     *
+     * This method takes the "BBBB" of versionCodes and compare them.
+     *
+     * https://www.chromium.org/developers/version-numbers describes general Chromium versioning;
+     * https://source.chromium.org/chromium/chromium/src/+/master:build/util/android_chrome_version.py
+     * is the canonical source for how Chromium versionCodes are calculated.
+     *
+     * @return true if versionCode1 is higher than or equal to versionCode2.
+     */
+    private static boolean versionCodeGE(long versionCode1, long versionCode2) {
+        long v1 = versionCode1 / 100000;
+        long v2 = versionCode2 / 100000;
+
+        return v1 >= v2;
+    }
+
+    /**
+     * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
+     * of all available-by-default WebView provider packages. If there is no such WebView provider
+     * package on the system, then return -1, which means all positive versionCode WebView packages
+     * are accepted.
+     *
+     * Note that this is a private method that handles a variable (mMinimumVersionCode) which is
+     * shared between threads. Furthermore, this method does not hold mLock meaning that we must
+     * take extra care to ensure this method is thread-safe.
+     */
+    private long getMinimumVersionCode() {
+        if (mMinimumVersionCode > 0) {
+            return mMinimumVersionCode;
+        }
+
+        long minimumVersionCode = -1;
+        for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+            if (provider.availableByDefault) {
+                try {
+                    long versionCode =
+                            mSystemInterface.getFactoryPackageVersion(provider.packageName);
+                    if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
+                        minimumVersionCode = versionCode;
+                    }
+                } catch (NameNotFoundException e) {
+                    // Safe to ignore.
+                }
+            }
+        }
+
+        mMinimumVersionCode = minimumVersionCode;
+        return mMinimumVersionCode;
+    }
+
+    private static boolean providerHasValidSignature(WebViewProviderInfo provider,
+            PackageInfo packageInfo, SystemInterface systemInterface) {
+        // Skip checking signatures on debuggable builds, for development purposes.
+        if (systemInterface.systemIsDebuggable()) return true;
+
+        // Allow system apps to be valid providers regardless of signature.
+        if (packageInfo.applicationInfo.isSystemApp()) return true;
+
+        // We don't support packages with multiple signatures.
+        if (packageInfo.signatures.length != 1) return false;
+
+        // If any of the declared signatures match the package signature, it's valid.
+        for (Signature signature : provider.signatures) {
+            if (signature.equals(packageInfo.signatures[0])) return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the only fallback provider in the set of given packages, or null if there is none.
+     */
+    private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
+        for (WebViewProviderInfo provider : webviewPackages) {
+            if (provider.isFallback) {
+                return provider;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isMultiProcessEnabled() {
+        int settingValue = mSystemInterface.getMultiProcessSetting(mContext);
+        if (mSystemInterface.isMultiProcessDefaultEnabled()) {
+            // Multiprocess should be enabled unless the user has turned it off manually.
+            return settingValue > MULTIPROCESS_SETTING_OFF_VALUE;
+        } else {
+            // Multiprocess should not be enabled, unless the user has turned it on manually.
+            return settingValue >= MULTIPROCESS_SETTING_ON_VALUE;
+        }
+    }
+
+    @Override
+    public void enableMultiProcess(boolean enable) {
+        PackageInfo current = getCurrentWebViewPackage();
+        mSystemInterface.setMultiProcessSetting(mContext,
+                enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE);
+        mSystemInterface.notifyZygote(enable);
+        if (current != null) {
+            mSystemInterface.killPackageDependents(current.packageName);
+        }
+    }
+
+    /**
+     * Dump the state of this Service.
+     */
+    @Override
+    public void dumpState(PrintWriter pw) {
+        pw.println("Current WebView Update Service state");
+        pw.println(String.format("  Multiprocess enabled: %b", isMultiProcessEnabled()));
+        synchronized (mLock) {
+            if (mCurrentWebViewPackage == null) {
+                pw.println("  Current WebView package is null");
+            } else {
+                pw.println(String.format("  Current WebView package (name, version): (%s, %s)",
+                        mCurrentWebViewPackage.packageName,
+                        mCurrentWebViewPackage.versionName));
+            }
+            pw.println(String.format("  Minimum targetSdkVersion: %d",
+                    UserPackage.MINIMUM_SUPPORTED_SDK));
+            pw.println(String.format("  Minimum WebView version code: %d",
+                    mMinimumVersionCode));
+            pw.println(String.format("  Number of relros started: %d",
+                    mNumRelroCreationsStarted));
+            pw.println(String.format("  Number of relros finished: %d",
+                        mNumRelroCreationsFinished));
+            pw.println(String.format("  WebView package dirty: %b", mWebViewPackageDirty));
+            pw.println(String.format("  Any WebView package installed: %b",
+                    mAnyWebViewInstalled));
+
+            try {
+                PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
+                pw.println(String.format(
+                        "  Preferred WebView package (name, version): (%s, %s)",
+                        preferredWebViewPackage.packageName,
+                        preferredWebViewPackage.versionName));
+            } catch (WebViewPackageMissingException e) {
+                pw.println(String.format("  Preferred WebView package: none"));
+            }
+
+            dumpAllPackageInformationLocked(pw);
+        }
+    }
+
+    private void dumpAllPackageInformationLocked(PrintWriter pw) {
+        WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
+        pw.println("  WebView packages:");
+        for (WebViewProviderInfo provider : allProviders) {
+            List<UserPackage> userPackages =
+                    mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
+            PackageInfo systemUserPackageInfo =
+                    userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
+            if (systemUserPackageInfo == null) {
+                pw.println(String.format("    %s is NOT installed.", provider.packageName));
+                continue;
+            }
+
+            int validity = validityResult(provider, systemUserPackageInfo);
+            String packageDetails = String.format(
+                    "versionName: %s, versionCode: %d, targetSdkVersion: %d",
+                    systemUserPackageInfo.versionName,
+                    systemUserPackageInfo.getLongVersionCode(),
+                    systemUserPackageInfo.applicationInfo.targetSdkVersion);
+            if (validity == VALIDITY_OK) {
+                boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
+                        mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider));
+                pw.println(String.format(
+                        "    Valid package %s (%s) is %s installed/enabled for all users",
+                        systemUserPackageInfo.packageName,
+                        packageDetails,
+                        installedForAllUsers ? "" : "NOT"));
+            } else {
+                pw.println(String.format("    Invalid package %s (%s), reason: %s",
+                        systemUserPackageInfo.packageName,
+                        packageDetails,
+                        getInvalidityReason(validity)));
+            }
+        }
+    }
+
+    private static String getInvalidityReason(int invalidityReason) {
+        switch (invalidityReason) {
+            case VALIDITY_INCORRECT_SDK_VERSION:
+                return "SDK version too low";
+            case VALIDITY_INCORRECT_VERSION_CODE:
+                return "Version code too low";
+            case VALIDITY_INCORRECT_SIGNATURE:
+                return "Incorrect signature";
+            case VALIDITY_NO_LIBRARY_FLAG:
+                return "No WebView-library manifest flag";
+            default:
+                return "Unexcepted validity-reason";
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
new file mode 100644
index 0000000..a9c3dc4
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.webkit;
+
+import android.content.pm.PackageInfo;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewProviderResponse;
+
+import java.io.PrintWriter;
+
+interface WebViewUpdateServiceInterface {
+    void packageStateChanged(String packageName, int changedState, int userId);
+
+    void handleNewUser(int userId);
+
+    void handleUserRemoved(int userId);
+
+    WebViewProviderInfo[] getWebViewPackages();
+
+    void prepareWebViewInSystemServer();
+
+    void notifyRelroCreationCompleted();
+
+    WebViewProviderResponse waitForAndGetProvider();
+
+    String changeProviderAndSetting(String newProviderName);
+
+    WebViewProviderInfo[] getValidWebViewPackages();
+
+    PackageInfo getCurrentWebViewPackage();
+
+    boolean isMultiProcessEnabled();
+
+    void enableMultiProcess(boolean enable);
+
+    void dumpState(PrintWriter pw);
+}
diff --git a/services/core/java/com/android/server/webkit/flags.aconfig b/services/core/java/com/android/server/webkit/flags.aconfig
new file mode 100644
index 0000000..1411acc
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.webkit"
+
+flag {
+    name: "update_service_v2"
+    namespace: "webview"
+    description: "Using a new version of the WebView update service"
+    bug: "308907090"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index faccca8..315e7d8 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -55,6 +55,7 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller;
+import static com.android.window.flags.Flags.allowDisableActivityRecordInputSink;
 
 import android.Manifest;
 import android.annotation.ColorInt;
@@ -1688,4 +1689,20 @@
             return r.mRequestedLaunchingTaskFragmentToken == taskFragmentToken;
         }
     }
+
+    @Override
+    public void setActivityRecordInputSinkEnabled(IBinder activityToken, boolean enabled) {
+        if (!allowDisableActivityRecordInputSink()) {
+            return;
+        }
+
+        mService.mAmInternal.enforceCallingPermission(
+                Manifest.permission.INTERNAL_SYSTEM_WINDOW, "setActivityRecordInputSinkEnabled");
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
+            if (r != null) {
+                r.mActivityRecordInputSinkEnabled = enabled;
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 081759d..d90d4ff 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -970,6 +970,8 @@
     boolean mWaitForEnteringPinnedMode;
 
     final ActivityRecordInputSink mActivityRecordInputSink;
+    // System activities with INTERNAL_SYSTEM_WINDOW can disable ActivityRecordInputSink.
+    boolean mActivityRecordInputSinkEnabled = true;
 
     // Activities with this uid are allowed to not create an input sink while being in the same
     // task and directly above this ActivityRecord. This field is updated whenever a new activity
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index be7d9b6..c61d863 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -86,7 +86,8 @@
         final boolean allowPassthrough = activityBelowInTask != null && (
                 activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid()
                         || activityBelowInTask.isUid(mActivityRecord.getUid()));
-        if (allowPassthrough || !mIsCompatEnabled || mActivityRecord.isInTransition()) {
+        if (allowPassthrough || !mIsCompatEnabled || mActivityRecord.isInTransition()
+                || !mActivityRecord.mActivityRecordInputSinkEnabled) {
             mInputWindowHandleWrapper.setInputConfigMasked(InputConfig.NOT_TOUCHABLE,
                     InputConfig.NOT_TOUCHABLE);
         } else {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a9f11e4..34c7eee 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -281,6 +281,7 @@
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wallpaper.WallpaperManagerInternal;
+import com.android.wm.shell.Flags;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -316,8 +317,6 @@
 public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
     private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
-    private static final String ENABLE_PIP2_IMPLEMENTATION =
-            "persist.wm.debug.enable_pip2_implementation";
     static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
     static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
 
@@ -7262,6 +7261,6 @@
     }
 
     static boolean isPip2ExperimentEnabled() {
-        return SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false);
+        return Flags.enablePip2Implementation();
     }
 }
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 287aaf9..8cc197c 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -46,7 +46,6 @@
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
-import android.compat.annotation.Overridable;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -88,7 +87,6 @@
     /** If enabled the creator will not allow BAL on its behalf by default. */
     @ChangeId
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
-    @Overridable
     private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR =
             296478951;
     public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED =
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index 28f656e..8b282dd3 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -58,6 +58,23 @@
     }
 
     /**
+     * Similar to {@link #scheduleTransactionItem}, but is called without WM lock.
+     *
+     * @see WindowProcessController#setReportedProcState(int)
+     */
+    void scheduleTransactionItemUnlocked(@NonNull IApplicationThread client,
+            @NonNull ClientTransactionItem transactionItem) throws RemoteException {
+        // Immediately dispatching to client, and must not access WMS.
+        final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
+        if (transactionItem.isActivityLifecycleItem()) {
+            clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
+        } else {
+            clientTransaction.addCallback(transactionItem);
+        }
+        scheduleTransaction(clientTransaction);
+    }
+
+    /**
      * Schedules a single transaction item, either a callback or a lifecycle request, delivery to
      * client application.
      * @throws RemoteException
@@ -65,6 +82,7 @@
      */
     void scheduleTransactionItem(@NonNull IApplicationThread client,
             @NonNull ClientTransactionItem transactionItem) throws RemoteException {
+        // TODO(b/260873529): queue the transaction items.
         final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
         if (transactionItem.isActivityLifecycleItem()) {
             clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
@@ -82,6 +100,7 @@
     void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client,
             @NonNull ClientTransactionItem transactionItem,
             @NonNull ActivityLifecycleItem lifecycleItem) throws RemoteException {
+        // TODO(b/260873529): replace with #scheduleTransactionItem after launch for cleanup.
         final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
         clientTransaction.addCallback(transactionItem);
         clientTransaction.setLifecycleStateRequest(lifecycleItem);
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index d0d7f49..e6bbd52 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -101,10 +101,8 @@
         mPolicy = displayContent.getDisplayPolicy();
         final Resources r = mPolicy.getContext().getResources();
         mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard);
-        mTransientControlTarget = new ControlTarget(
-                stateController, displayContent.mWmService.mH, "TransientControlTarget");
-        mPermanentControlTarget = new ControlTarget(
-                stateController, displayContent.mWmService.mH, "PermanentControlTarget");
+        mTransientControlTarget = new ControlTarget(displayContent, "TransientControlTarget");
+        mPermanentControlTarget = new ControlTarget(displayContent, "PermanentControlTarget");
     }
 
     /** Updates the target which can control system bars. */
@@ -699,24 +697,35 @@
         }
     }
 
-    private static class ControlTarget implements InsetsControlTarget {
+    private static class ControlTarget implements InsetsControlTarget, Runnable {
 
+        private final Handler mHandler;
+        private final Object mGlobalLock;
         private final InsetsState mState = new InsetsState();
-        private final InsetsController mInsetsController;
         private final InsetsStateController mStateController;
+        private final InsetsController mInsetsController;
         private final String mName;
 
-        ControlTarget(InsetsStateController stateController, Handler handler, String name) {
-            mStateController = stateController;
-            mInsetsController = new InsetsController(new Host(handler, name));
+        ControlTarget(DisplayContent displayContent, String name) {
+            mHandler = displayContent.mWmService.mH;
+            mGlobalLock = displayContent.mWmService.mGlobalLock;
+            mStateController = displayContent.getInsetsStateController();
+            mInsetsController = new InsetsController(new Host(mHandler, name));
             mName = name;
         }
 
         @Override
         public void notifyInsetsControlChanged() {
-            mState.set(mStateController.getRawInsetsState(), true /* copySources */);
-            mInsetsController.onStateChanged(mState);
-            mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this));
+            mHandler.post(this);
+        }
+
+        @Override
+        public void run() {
+            synchronized (mGlobalLock) {
+                mState.set(mStateController.getRawInsetsState(), true /* copySources */);
+                mInsetsController.onStateChanged(mState);
+                mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this));
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java
index b3a3650..89a70e5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerFlags.java
+++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java
@@ -43,8 +43,6 @@
 
     /* Start Available Flags */
 
-    final boolean mSyncWindowConfigUpdateFlag = Flags.syncWindowConfigUpdateFlag();
-
     final boolean mWindowStateResizeItemFlag = Flags.windowStateResizeItemFlag();
 
     final boolean mWallpaperOffsetAsync = Flags.wallpaperOffsetAsync();
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 558bf9d..2b18f07 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -388,13 +388,22 @@
         final IApplicationThread thread = mThread;
         if (prevProcState >= CACHED_CONFIG_PROC_STATE && repProcState < CACHED_CONFIG_PROC_STATE
                 && thread != null && mHasCachedConfiguration) {
-            final Configuration config;
+            final ConfigurationChangeItem configurationChangeItem;
             synchronized (mLastReportedConfiguration) {
-                config = new Configuration(mLastReportedConfiguration);
+                onConfigurationChangePreScheduled(mLastReportedConfiguration);
+                configurationChangeItem = ConfigurationChangeItem.obtain(
+                        mLastReportedConfiguration, mLastTopActivityDeviceId);
             }
             // Schedule immediately to make sure the app component (e.g. receiver, service) can get
             // the latest configuration in their lifecycle callbacks (e.g. onReceive, onCreate).
-            scheduleConfigurationChange(thread, config);
+            try {
+                // No WM lock here.
+                mAtm.getLifecycleManager().scheduleTransactionItemUnlocked(
+                        thread, configurationChangeItem);
+            } catch (Exception e) {
+                Slog.e(TAG_CONFIGURATION, "Failed to schedule ConfigurationChangeItem="
+                        + configurationChangeItem + " owner=" + mOwner, e);
+            }
         }
     }
 
@@ -1634,11 +1643,12 @@
             }
         }
 
-        scheduleConfigurationChange(thread, config);
+        onConfigurationChangePreScheduled(config);
+        scheduleClientTransactionItem(thread, ConfigurationChangeItem.obtain(
+                config, mLastTopActivityDeviceId));
     }
 
-    private void scheduleConfigurationChange(@NonNull IApplicationThread thread,
-            @NonNull Configuration config) {
+    private void onConfigurationChangePreScheduled(@NonNull Configuration config) {
         ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName,
                 config);
         if (Build.IS_DEBUGGABLE && mHasImeService) {
@@ -1646,8 +1656,6 @@
             Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config);
         }
         mHasCachedConfiguration = false;
-        scheduleClientTransactionItem(thread, ConfigurationChangeItem.obtain(
-                config, mLastTopActivityDeviceId));
     }
 
     @VisibleForTesting
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 0d196b4..7c53950 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -2175,9 +2175,9 @@
 
                     userState.appIdDevicePermissionFlags[appId]?.forEachIndexed {
                         _,
-                        deviceId,
+                        persistentDeviceId,
                         devicePermissionFlags ->
-                        println("Permissions (Device $deviceId):")
+                        println("Permissions (Device $persistentDeviceId):")
                         withIndent {
                             devicePermissionFlags.forEachIndexed { _, permissionName, flags ->
                                 val isGranted = PermissionFlags.isPermissionGranted(flags)
@@ -2658,7 +2658,7 @@
         override fun onDevicePermissionFlagsChanged(
             appId: Int,
             userId: Int,
-            deviceId: String,
+            persistentDeviceId: String,
             permissionName: String,
             oldFlags: Int,
             newFlags: Int
@@ -2681,7 +2681,8 @@
                         permissionName in NOTIFICATIONS_PERMISSIONS &&
                             runtimePermissionRevokedUids.get(uid, true)
                 }
-                runtimePermissionChangedUidDevices.getOrPut(uid) { mutableSetOf() } += deviceId
+                runtimePermissionChangedUidDevices
+                    .getOrPut(uid) { mutableSetOf() } += persistentDeviceId
             }
 
             if (permission.hasGids && !wasPermissionGranted && isPermissionGranted) {
@@ -2695,9 +2696,9 @@
                 isPermissionFlagsChanged = false
             }
 
-            runtimePermissionChangedUidDevices.forEachIndexed { _, uid, deviceIds ->
-                deviceIds.forEach { deviceId ->
-                    onPermissionsChangeListeners.onPermissionsChanged(uid, deviceId)
+            runtimePermissionChangedUidDevices.forEachIndexed { _, uid, persistentDeviceIds ->
+                persistentDeviceIds.forEach { persistentDeviceId ->
+                    onPermissionsChangeListeners.onPermissionsChanged(uid, persistentDeviceId)
                 }
             }
             runtimePermissionChangedUidDevices.clear()
@@ -2772,16 +2773,16 @@
             when (msg.what) {
                 MSG_ON_PERMISSIONS_CHANGED -> {
                     val uid = msg.arg1
-                    val deviceId = msg.obj as String
-                    handleOnPermissionsChanged(uid, deviceId)
+                    val persistentDeviceId = msg.obj as String
+                    handleOnPermissionsChanged(uid, persistentDeviceId)
                 }
             }
         }
 
-        private fun handleOnPermissionsChanged(uid: Int, deviceId: String) {
+        private fun handleOnPermissionsChanged(uid: Int, persistentDeviceId: String) {
             listeners.broadcast { listener ->
                 try {
-                    listener.onPermissionsChanged(uid, deviceId)
+                    listener.onPermissionsChanged(uid, persistentDeviceId)
                 } catch (e: RemoteException) {
                     Slog.e(LOG_TAG, "Error when calling OnPermissionsChangeListener", e)
                 }
@@ -2796,9 +2797,10 @@
             listeners.unregister(listener)
         }
 
-        fun onPermissionsChanged(uid: Int, deviceId: String) {
+        fun onPermissionsChanged(uid: Int, persistentDeviceId: String) {
             if (listeners.registeredCallbackCount > 0) {
-                obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0, deviceId).sendToTarget()
+                obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0, persistentDeviceId)
+                    .sendToTarget()
             }
         }
 
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
index 6c24d6d..820628c 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
@@ -21,8 +21,8 @@
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="install-arg" value="-t" />
-        <option name="test-file-name" value="FrameworksImeTests.apk" />
         <option name="test-file-name" value="SimpleTestIme.apk" />
+        <option name="test-file-name" value="FrameworksImeTests.apk" />
     </target_preparer>
 
     <option name="test-tag" value="FrameworksImeTests" />
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
index a400f12..eb6e8b4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -44,6 +44,7 @@
         float brightness = 0.3f;
         float sdrBrightness = 0.2f;
         boolean shouldUseAutoBrightness = true;
+        boolean shouldUpdateScreenBrightnessSetting = true;
         BrightnessReason brightnessReason = new BrightnessReason();
         brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC);
         brightnessReason.setModifier(BrightnessReason.MODIFIER_DIMMED);
@@ -52,12 +53,15 @@
                 .setSdrBrightness(sdrBrightness)
                 .setBrightnessReason(brightnessReason)
                 .setShouldUseAutoBrightness(shouldUseAutoBrightness)
+                .setShouldUpdateScreenBrightnessSetting(shouldUpdateScreenBrightnessSetting)
                 .build();
 
         assertEquals(displayBrightnessState.getBrightness(), brightness, FLOAT_DELTA);
         assertEquals(displayBrightnessState.getSdrBrightness(), sdrBrightness, FLOAT_DELTA);
         assertEquals(displayBrightnessState.getBrightnessReason(), brightnessReason);
         assertEquals(displayBrightnessState.getShouldUseAutoBrightness(), shouldUseAutoBrightness);
+        assertEquals(shouldUpdateScreenBrightnessSetting,
+                displayBrightnessState.shouldUpdateScreenBrightnessSetting());
         assertEquals(displayBrightnessState.toString(), getString(displayBrightnessState));
     }
 
@@ -71,6 +75,7 @@
                 .setBrightness(0.26f)
                 .setSdrBrightness(0.23f)
                 .setShouldUseAutoBrightness(false)
+                .setShouldUpdateScreenBrightnessSetting(true)
                 .build();
         DisplayBrightnessState state2 = DisplayBrightnessState.Builder.from(state1).build();
         assertEquals(state1, state2);
@@ -92,7 +97,9 @@
                 .append("\n    maxBrightness:")
                 .append(displayBrightnessState.getMaxBrightness())
                 .append("\n    customAnimationRate:")
-                .append(displayBrightnessState.getCustomAnimationRate());
+                .append(displayBrightnessState.getCustomAnimationRate())
+                .append("\n    shouldUpdateScreenBrightnessSetting:")
+                .append(displayBrightnessState.shouldUpdateScreenBrightnessSetting());
         return sb.toString();
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 0bf4654..353a7bb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -2754,8 +2754,7 @@
         DisplayOffloader mockDisplayOffloader = mock(DisplayOffloader.class);
         localService.registerDisplayOffloader(displayId, mockDisplayOffloader);
 
-        assertThat(display.getDisplayOffloadSessionLocked().getDisplayOffloader()).isEqualTo(
-                mockDisplayOffloader);
+        assertThat(display.getDisplayOffloadSessionLocked()).isNotNull();
     }
 
     @Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
new file mode 100644
index 0000000..dea838d
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class DisplayOffloadSessionImplTest {
+
+    @Mock
+    private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
+
+    @Mock
+    private DisplayPowerControllerInterface mDisplayPowerController;
+
+    private DisplayOffloadSessionImpl mSession;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mDisplayOffloader.startOffload()).thenReturn(true);
+        mSession = new DisplayOffloadSessionImpl(mDisplayOffloader, mDisplayPowerController);
+    }
+
+    @Test
+    public void testStartOffload() {
+        mSession.startOffload();
+        assertTrue(mSession.isActive());
+
+        // An active session shouldn't be started again
+        mSession.startOffload();
+        verify(mDisplayOffloader, times(1)).startOffload();
+    }
+
+    @Test
+    public void testStopOffload() {
+        mSession.startOffload();
+        mSession.stopOffload();
+
+        assertFalse(mSession.isActive());
+        verify(mDisplayPowerController).setBrightnessFromOffload(
+                PowerManager.BRIGHTNESS_INVALID_FLOAT);
+
+        // An inactive session shouldn't be stopped again
+        mSession.stopOffload();
+        verify(mDisplayOffloader, times(1)).stopOffload();
+    }
+
+    @Test
+    public void testUpdateBrightness_sessionInactive() {
+        mSession.updateBrightness(0.3f);
+        verify(mDisplayPowerController, never()).setBrightnessFromOffload(anyFloat());
+    }
+
+    @Test
+    public void testUpdateBrightness_sessionActive() {
+        float brightness = 0.3f;
+
+        mSession.startOffload();
+        mSession.updateBrightness(brightness);
+
+        verify(mDisplayPowerController).setBrightnessFromOffload(brightness);
+    }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index 57f392a..693cafe 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -127,8 +127,6 @@
     private Handler mHandler;
     private DisplayPowerControllerHolder mHolder;
     private Sensor mProxSensor;
-    private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
-    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
 
     @Mock
     private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
@@ -148,6 +146,8 @@
     private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock;
     @Mock
     private DisplayManagerFlags mDisplayManagerFlagsMock;
+    @Mock
+    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
     @Captor
     private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
 
@@ -1160,19 +1160,19 @@
                 any(AutomaticBrightnessController.Callbacks.class),
                 any(Looper.class),
                 eq(mSensorManagerMock),
-                any(),
+                /* lightSensor= */ any(),
                 eq(mHolder.brightnessMappingStrategy),
-                anyInt(),
-                anyFloat(),
-                anyFloat(),
-                anyFloat(),
-                anyInt(),
-                anyInt(),
-                anyLong(),
-                anyLong(),
-                anyLong(),
-                anyLong(),
-                anyBoolean(),
+                /* lightSensorWarmUpTime= */ anyInt(),
+                /* brightnessMin= */ anyFloat(),
+                /* brightnessMax= */ anyFloat(),
+                /* dozeScaleFactor */ anyFloat(),
+                /* lightSensorRate= */ anyInt(),
+                /* initialLightSensorRate= */ anyInt(),
+                /* brighteningLightDebounceConfig */ anyLong(),
+                /* darkeningLightDebounceConfig */ anyLong(),
+                /* brighteningLightDebounceConfigIdle= */ anyLong(),
+                /* darkeningLightDebounceConfigIdle= */ anyLong(),
+                /* resetAmbientLuxAfterWarmUpConfig= */ anyBoolean(),
                 any(HysteresisLevels.class),
                 any(HysteresisLevels.class),
                 any(HysteresisLevels.class),
@@ -1180,9 +1180,9 @@
                 eq(mContext),
                 any(BrightnessRangeController.class),
                 any(BrightnessThrottler.class),
-                isNull(),
-                anyInt(),
-                anyInt(),
+                /* idleModeBrightnessMapper= */ isNull(),
+                /* ambientLightHorizonShort= */ anyInt(),
+                /* ambientLightHorizonLong= */ anyInt(),
                 eq(lux),
                 eq(brightness)
         );
@@ -1294,9 +1294,8 @@
     public void testRampRateForHdrContent_HdrClamperOn() {
         float clampedBrightness = 0.6f;
         float transitionRate = 1.5f;
-        DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
-        when(flags.isHdrClamperEnabled()).thenReturn(true);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags);
+        when(mDisplayManagerFlagsMock.isHdrClamperEnabled()).thenReturn(true);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
 
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
@@ -1392,9 +1391,8 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
     public void testRampMaxTimeInteractiveThenIdle_DifferentValues() {
-        DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
-        when(flags.isAdaptiveTone1Enabled()).thenReturn(true);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags);
+        when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
 
         // Send a display power request
         DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -1447,9 +1445,8 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
     public void testRampMaxTimeIdle_DifferentValues() {
-        DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
-        when(flags.isAdaptiveTone1Enabled()).thenReturn(true);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags);
+        when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
 
         // Send a display power request
         DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -1481,8 +1478,6 @@
             when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
             return null;
         }).when(mHolder.displayPowerState).setScreenState(anyInt());
-        // init displayoffload session and support offloading.
-        initDisplayOffloadSession();
         mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
 
         // start with DOZE.
@@ -1509,8 +1504,6 @@
             when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
             return null;
         }).when(mHolder.displayPowerState).setScreenState(anyInt());
-        // init displayoffload session and support offloading.
-        initDisplayOffloadSession();
         mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
 
         // start with DOZE.
@@ -1536,8 +1529,6 @@
             when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
             return null;
         }).when(mHolder.displayPowerState).setScreenState(anyInt());
-        // init displayoffload session and support offloading.
-        initDisplayOffloadSession();
         mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
 
         // start with OFF.
@@ -1553,26 +1544,29 @@
         verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
     }
 
-    private void initDisplayOffloadSession() {
-        mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() {
-            @Override
-            public boolean startOffload() {
-                return true;
-            }
+    @Test
+    public void testBrightnessFromOffload() {
+        when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+        float brightness = 0.34f;
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
 
-            @Override
-            public void stopOffload() {}
-        });
+        mHolder.dpc.setBrightnessFromOffload(brightness);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
 
-        mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() {
-            @Override
-            public void setDozeStateOverride(int displayState) {}
-
-            @Override
-            public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() {
-                return mDisplayOffloader;
-            }
-        };
+        // One triggered by handleBrightnessModeChange, another triggered by
+        // setBrightnessFromOffload
+        verify(mHolder.animator, times(2)).animateTo(eq(brightness), anyFloat(),
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     /**
@@ -1659,12 +1653,6 @@
 
     private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
             String uniqueId, boolean isEnabled) {
-        return createDisplayPowerController(displayId, uniqueId, isEnabled,
-                mock(DisplayManagerFlags.class));
-    }
-
-    private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
-            String uniqueId, boolean isEnabled, DisplayManagerFlags flags) {
         final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
         final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class);
         final AutomaticBrightnessController automaticBrightnessController =
@@ -1690,7 +1678,7 @@
         TestInjector injector = spy(new TestInjector(displayPowerState, animator,
                 automaticBrightnessController, wakelockController, brightnessMappingStrategy,
                 hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper,
-                clamperController, flags));
+                clamperController, mDisplayManagerFlagsMock));
 
         final LogicalDisplay display = mock(LogicalDisplay.class);
         final DisplayDevice device = mock(DisplayDevice.class);
@@ -1705,7 +1693,7 @@
                 mSensorManagerMock, mDisplayBlankerMock, display,
                 mBrightnessTrackerMock, brightnessSetting, () -> {
         },
-                hbmMetadata, /* bootCompleted= */ false, flags);
+                hbmMetadata, /* bootCompleted= */ false, mDisplayManagerFlagsMock);
 
         return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
                 animator, automaticBrightnessController, wakelockController,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 9617bd0..b227993 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -125,8 +125,6 @@
     private Handler mHandler;
     private DisplayPowerControllerHolder mHolder;
     private Sensor mProxSensor;
-    private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
-    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
 
     @Mock
     private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
@@ -146,6 +144,8 @@
     private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock;
     @Mock
     private DisplayManagerFlags mDisplayManagerFlagsMock;
+    @Mock
+    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
 
     @Captor
     private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
@@ -1094,19 +1094,19 @@
                 any(AutomaticBrightnessController.Callbacks.class),
                 any(Looper.class),
                 eq(mSensorManagerMock),
-                any(),
+                /* lightSensor= */ any(),
                 eq(mHolder.brightnessMappingStrategy),
-                anyInt(),
-                anyFloat(),
-                anyFloat(),
-                anyFloat(),
-                anyInt(),
-                anyInt(),
-                anyLong(),
-                anyLong(),
-                anyLong(),
-                anyLong(),
-                anyBoolean(),
+                /* lightSensorWarmUpTime= */ anyInt(),
+                /* brightnessMin= */ anyFloat(),
+                /* brightnessMax= */ anyFloat(),
+                /* dozeScaleFactor */ anyFloat(),
+                /* lightSensorRate= */ anyInt(),
+                /* initialLightSensorRate= */ anyInt(),
+                /* brighteningLightDebounceConfig */ anyLong(),
+                /* darkeningLightDebounceConfig */ anyLong(),
+                /* brighteningLightDebounceConfigIdle= */ anyLong(),
+                /* darkeningLightDebounceConfigIdle= */ anyLong(),
+                /* resetAmbientLuxAfterWarmUpConfig= */ anyBoolean(),
                 any(HysteresisLevels.class),
                 any(HysteresisLevels.class),
                 any(HysteresisLevels.class),
@@ -1114,9 +1114,9 @@
                 eq(mContext),
                 any(BrightnessRangeController.class),
                 any(BrightnessThrottler.class),
-                isNull(),
-                anyInt(),
-                anyInt(),
+                /* idleModeBrightnessMapper= */ isNull(),
+                /* ambientLightHorizonShort= */ anyInt(),
+                /* ambientLightHorizonLong= */ anyInt(),
                 eq(lux),
                 eq(brightness)
         );
@@ -1386,8 +1386,6 @@
             when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
             return null;
         }).when(mHolder.displayPowerState).setScreenState(anyInt());
-        // init displayoffload session and support offloading.
-        initDisplayOffloadSession();
         mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
 
         // start with DOZE.
@@ -1414,8 +1412,6 @@
             when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
             return null;
         }).when(mHolder.displayPowerState).setScreenState(anyInt());
-        // init displayoffload session and support offloading.
-        initDisplayOffloadSession();
         mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
 
         // start with DOZE.
@@ -1441,8 +1437,6 @@
             when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
             return null;
         }).when(mHolder.displayPowerState).setScreenState(anyInt());
-        // init displayoffload session and support offloading.
-        initDisplayOffloadSession();
         mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
 
         // start with OFF.
@@ -1458,28 +1452,6 @@
         verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
     }
 
-    private void initDisplayOffloadSession() {
-        mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() {
-            @Override
-            public boolean startOffload() {
-                return true;
-            }
-
-            @Override
-            public void stopOffload() {}
-        });
-
-        mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() {
-            @Override
-            public void setDozeStateOverride(int displayState) {}
-
-            @Override
-            public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() {
-                return mDisplayOffloader;
-            }
-        };
-    }
-
     private void advanceTime(long timeMs) {
         mClock.fastForward(timeMs);
         mTestLooper.dispatchAll();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index a77a958..8270845 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -25,6 +25,7 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
@@ -40,12 +41,12 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
 import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.PowerManager;
 import android.view.Display;
 import android.view.DisplayAddress;
 import android.view.SurfaceControl;
@@ -118,10 +119,12 @@
     private DisplayNotificationManager mMockedDisplayNotificationManager;
     @Mock
     private DisplayManagerFlags mFlags;
+    @Mock
+    private DisplayPowerControllerInterface mMockedDisplayPowerController;
 
     private Handler mHandler;
 
-    private DisplayOffloadSession mDisplayOffloadSession;
+    private DisplayOffloadSessionImpl mDisplayOffloadSession;
 
     private DisplayOffloader mDisplayOffloader;
 
@@ -1195,6 +1198,7 @@
         }
 
         verify(mDisplayOffloader, times(mDisplayOffloadSupportedStates.size())).startOffload();
+        assertTrue(mDisplayOffloadSession.isActive());
     }
 
     @Test
@@ -1217,6 +1221,9 @@
         changeStateToDozeRunnable.run();
 
         verify(mDisplayOffloader).stopOffload();
+        assertFalse(mDisplayOffloadSession.isActive());
+        verify(mMockedDisplayPowerController).setBrightnessFromOffload(
+                PowerManager.BRIGHTNESS_INVALID_FLOAT);
     }
 
     private void initDisplayOffloadSession() {
@@ -1230,15 +1237,8 @@
             public void stopOffload() {}
         });
 
-        mDisplayOffloadSession = new DisplayOffloadSession() {
-            @Override
-            public void setDozeStateOverride(int displayState) {}
-
-            @Override
-            public DisplayOffloader getDisplayOffloader() {
-                return mDisplayOffloader;
-            }
-        };
+        mDisplayOffloadSession = new DisplayOffloadSessionImpl(mDisplayOffloader,
+                mMockedDisplayPowerController);
     }
 
     private void setupCutoutAndRoundedCorners() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index c4f4838..52fa91f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -42,7 +42,9 @@
 import com.android.server.display.AutomaticBrightnessController;
 import com.android.server.display.BrightnessSetting;
 import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
+import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
 import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -66,6 +68,8 @@
     private BrightnessSetting mBrightnessSetting;
     @Mock
     private Runnable mOnBrightnessChangeRunnable;
+    @Mock
+    private DisplayManagerFlags mDisplayManagerFlags;
 
     @Mock
     private HandlerExecutor mBrightnessChangeExecutor;
@@ -74,7 +78,7 @@
             DisplayBrightnessController.Injector() {
         @Override
         DisplayBrightnessStrategySelector getDisplayBrightnessStrategySelector(
-                Context context, int displayId) {
+                Context context, int displayId, DisplayManagerFlags flags) {
             return mDisplayBrightnessStrategySelector;
         }
     };
@@ -92,7 +96,7 @@
                 .thenReturn(true);
         mDisplayBrightnessController = new DisplayBrightnessController(mContext, mInjector,
                 DISPLAY_ID, DEFAULT_BRIGHTNESS, mBrightnessSetting, mOnBrightnessChangeRunnable,
-                mBrightnessChangeExecutor);
+                mBrightnessChangeExecutor, mDisplayManagerFlags);
     }
 
     @Test
@@ -350,7 +354,7 @@
         int nonDefaultDisplayId = 1;
         mDisplayBrightnessController = new DisplayBrightnessController(mContext, mInjector,
                 nonDefaultDisplayId, DEFAULT_BRIGHTNESS, mBrightnessSetting,
-                mOnBrightnessChangeRunnable, mBrightnessChangeExecutor);
+                mOnBrightnessChangeRunnable, mBrightnessChangeExecutor, mDisplayManagerFlags);
         brightness = 0.5f;
         when(mBrightnessSetting.getBrightness()).thenReturn(brightness);
         mDisplayBrightnessController.setAutomaticBrightnessController(
@@ -384,4 +388,14 @@
         verify(mBrightnessSetting).setBrightness(brightnessValue2);
         verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits2);
     }
+
+    @Test
+    public void setBrightnessFromOffload() {
+        float brightness = 0.4f;
+        OffloadBrightnessStrategy offloadBrightnessStrategy = mock(OffloadBrightnessStrategy.class);
+        when(mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy()).thenReturn(
+                offloadBrightnessStrategy);
+        mDisplayBrightnessController.setBrightnessFromOffload(brightness);
+        verify(offloadBrightnessStrategy).setOffloadScreenBrightness(brightness);
+    }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index 8497dab..37958fa 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -17,6 +17,7 @@
 package com.android.server.display.brightness;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
@@ -35,13 +36,16 @@
 import com.android.internal.R;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
 import com.android.server.display.brightness.strategy.BoostBrightnessStrategy;
 import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
 import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
 import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
+import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
 import com.android.server.display.brightness.strategy.OverrideBrightnessStrategy;
 import com.android.server.display.brightness.strategy.ScreenOffBrightnessStrategy;
 import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -71,10 +75,64 @@
     @Mock
     private FollowerBrightnessStrategy mFollowerBrightnessStrategy;
     @Mock
+    private AutomaticBrightnessStrategy mAutomaticBrightnessStrategy;
+    @Mock
+    private OffloadBrightnessStrategy mOffloadBrightnessStrategy;
+    @Mock
     private Resources mResources;
+    @Mock
+    private DisplayManagerFlags mDisplayManagerFlags;
 
     private DisplayBrightnessStrategySelector mDisplayBrightnessStrategySelector;
     private Context mContext;
+    private DisplayBrightnessStrategySelector.Injector mInjector =
+            new DisplayBrightnessStrategySelector.Injector() {
+                @Override
+                ScreenOffBrightnessStrategy getScreenOffBrightnessStrategy() {
+                    return mScreenOffBrightnessModeStrategy;
+                }
+
+                @Override
+                DozeBrightnessStrategy getDozeBrightnessStrategy() {
+                    return mDozeBrightnessModeStrategy;
+                }
+
+                @Override
+                OverrideBrightnessStrategy getOverrideBrightnessStrategy() {
+                    return mOverrideBrightnessStrategy;
+                }
+
+                @Override
+                TemporaryBrightnessStrategy getTemporaryBrightnessStrategy() {
+                    return mTemporaryBrightnessStrategy;
+                }
+
+                @Override
+                BoostBrightnessStrategy getBoostBrightnessStrategy() {
+                    return mBoostBrightnessStrategy;
+                }
+
+                @Override
+                FollowerBrightnessStrategy getFollowerBrightnessStrategy(int displayId) {
+                    return mFollowerBrightnessStrategy;
+                }
+
+                @Override
+                InvalidBrightnessStrategy getInvalidBrightnessStrategy() {
+                    return mInvalidBrightnessStrategy;
+                }
+
+                @Override
+                AutomaticBrightnessStrategy getAutomaticBrightnessStrategy(Context context,
+                        int displayId) {
+                    return mAutomaticBrightnessStrategy;
+                }
+
+                @Override
+                OffloadBrightnessStrategy getOffloadBrightnessStrategy() {
+                    return mOffloadBrightnessStrategy;
+                }
+            };
 
     @Rule
     public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@@ -87,45 +145,8 @@
         when(mContext.getContentResolver()).thenReturn(contentResolver);
         when(mContext.getResources()).thenReturn(mResources);
         when(mInvalidBrightnessStrategy.getName()).thenReturn("InvalidBrightnessStrategy");
-        DisplayBrightnessStrategySelector.Injector injector =
-                new DisplayBrightnessStrategySelector.Injector() {
-                    @Override
-                    ScreenOffBrightnessStrategy getScreenOffBrightnessStrategy() {
-                        return mScreenOffBrightnessModeStrategy;
-                    }
-
-                    @Override
-                    DozeBrightnessStrategy getDozeBrightnessStrategy() {
-                        return mDozeBrightnessModeStrategy;
-                    }
-
-                    @Override
-                    OverrideBrightnessStrategy getOverrideBrightnessStrategy() {
-                        return mOverrideBrightnessStrategy;
-                    }
-
-                    @Override
-                    TemporaryBrightnessStrategy getTemporaryBrightnessStrategy() {
-                        return mTemporaryBrightnessStrategy;
-                    }
-
-                    @Override
-                    BoostBrightnessStrategy getBoostBrightnessStrategy() {
-                        return mBoostBrightnessStrategy;
-                    }
-
-                    @Override
-                    FollowerBrightnessStrategy getFollowerBrightnessStrategy(int displayId) {
-                        return mFollowerBrightnessStrategy;
-                    }
-
-                    @Override
-                    InvalidBrightnessStrategy getInvalidBrightnessStrategy() {
-                        return mInvalidBrightnessStrategy;
-                    }
-                };
         mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
-                injector, DISPLAY_ID);
+                mInjector, DISPLAY_ID, mDisplayManagerFlags);
 
     }
 
@@ -188,6 +209,7 @@
         displayPowerRequest.screenBrightnessOverride = Float.NaN;
         when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
         when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+        when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(Float.NaN);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
                 Display.STATE_ON), mInvalidBrightnessStrategy);
     }
@@ -200,4 +222,35 @@
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
                 Display.STATE_ON), mFollowerBrightnessStrategy);
     }
+
+    @Test
+    public void selectStrategySelectsOffloadStrategyWhenValid() {
+        when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
+        mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+                mInjector, DISPLAY_ID, mDisplayManagerFlags);
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+                DisplayManagerInternal.DisplayPowerRequest.class);
+        displayPowerRequest.screenBrightnessOverride = Float.NaN;
+        when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
+        when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+        when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f);
+        assertEquals(mOffloadBrightnessStrategy, mDisplayBrightnessStrategySelector.selectStrategy(
+                displayPowerRequest, Display.STATE_ON));
+    }
+
+    @Test
+    public void selectStrategyDoesNotSelectOffloadStrategyWhenFeatureFlagDisabled() {
+        when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(false);
+        mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+                mInjector, DISPLAY_ID, mDisplayManagerFlags);
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+                DisplayManagerInternal.DisplayPowerRequest.class);
+        displayPowerRequest.screenBrightnessOverride = Float.NaN;
+        when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
+        when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+        when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f);
+        assertNotEquals(mOffloadBrightnessStrategy,
+                mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
+                        Display.STATE_ON));
+    }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index b652576..78ec2ff3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -188,6 +188,29 @@
     }
 
     @Test
+    public void testAutoBrightnessState_BrightnessReasonIsOffload() {
+        mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
+        int targetDisplayState = Display.STATE_ON;
+        boolean allowAutoBrightnessWhileDozing = false;
+        int brightnessReason = BrightnessReason.REASON_OFFLOAD;
+        int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+        float lastUserSetBrightness = 0.2f;
+        boolean userSetBrightnessChanged = true;
+        mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(true);
+        mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+                userSetBrightnessChanged);
+        verify(mAutomaticBrightnessController)
+                .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
+                        mBrightnessConfiguration,
+                        lastUserSetBrightness,
+                        userSetBrightnessChanged, 0.5f,
+                        false, policy, true);
+        assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
+        assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
+    }
+
+    @Test
     public void testAutoBrightnessState_DisplayIsInDoze_ConfigDoesAllow() {
         mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
         int targetDisplayState = Display.STATE_DOZE;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
new file mode 100644
index 0000000..36719af
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+import static org.junit.Assert.assertEquals;
+
+import android.hardware.display.DisplayManagerInternal;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class OffloadBrightnessStrategyTest {
+
+    private OffloadBrightnessStrategy mOffloadBrightnessStrategy;
+
+    @Before
+    public void before() {
+        mOffloadBrightnessStrategy = new OffloadBrightnessStrategy();
+    }
+
+    @Test
+    public void testUpdateBrightnessWhenOffloadBrightnessIsSet() {
+        DisplayManagerInternal.DisplayPowerRequest
+                displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
+        float brightness = 0.2f;
+        mOffloadBrightnessStrategy.setOffloadScreenBrightness(brightness);
+        BrightnessReason brightnessReason = new BrightnessReason();
+        brightnessReason.setReason(BrightnessReason.REASON_OFFLOAD);
+        DisplayBrightnessState expectedDisplayBrightnessState =
+                new DisplayBrightnessState.Builder()
+                        .setBrightness(brightness)
+                        .setBrightnessReason(brightnessReason)
+                        .setSdrBrightness(brightness)
+                        .setDisplayBrightnessStrategyName(mOffloadBrightnessStrategy.getName())
+                        .setShouldUpdateScreenBrightnessSetting(true)
+                        .build();
+        DisplayBrightnessState updatedDisplayBrightnessState =
+                mOffloadBrightnessStrategy.updateBrightness(displayPowerRequest);
+        assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
index 2d760a7..1002fba 100644
--- a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
@@ -220,7 +220,7 @@
 
     private void setDeviceConfigPinnedAnonSize(long size) {
         mFakeDeviceConfigInterface.setProperty(
-                DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+                DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
                 "pin_shared_anon_size",
                 String.valueOf(size),
                 /*makeDefault=*/false);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 0f3daec..74eb79d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -28,6 +28,8 @@
 import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED_UI_SHOWING;
 import static com.android.server.biometrics.BiometricServiceStateProto.STATE_ERROR_PENDING_SYSUI;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -248,6 +250,45 @@
     }
 
     @Test
+    public void testOnErrorReceivedBeforeOnDialogAnimatedIn() throws RemoteException {
+        final int fingerprintId = 0;
+        final int faceId = 1;
+        setupFingerprint(fingerprintId, FingerprintSensorProperties.TYPE_REAR);
+        setupFace(faceId, true /* confirmationAlwaysRequired */,
+                mock(IBiometricAuthenticator.class));
+        final AuthSession session = createAuthSession(mSensors,
+                false /* checkDevicePolicyManager */,
+                Authenticators.BIOMETRIC_STRONG,
+                TEST_REQUEST_ID,
+                0 /* operationId */,
+                0 /* userId */);
+        session.goToInitialState();
+
+        for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+            assertThat(sensor.getSensorState()).isEqualTo(BiometricSensor.STATE_WAITING_FOR_COOKIE);
+            session.onCookieReceived(
+                    session.mPreAuthInfo.eligibleSensors.get(sensor.id).getCookie());
+        }
+        assertThat(session.allCookiesReceived()).isTrue();
+        assertThat(session.getState()).isEqualTo(STATE_AUTH_STARTED);
+
+        final BiometricSensor faceSensor = session.mPreAuthInfo.eligibleSensors.get(faceId);
+        final BiometricSensor fingerprintSensor = session.mPreAuthInfo.eligibleSensors.get(
+                fingerprintId);
+        final int cookie = faceSensor.getCookie();
+        session.onErrorReceived(0, cookie, BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL, 0);
+
+        assertThat(faceSensor.getSensorState()).isEqualTo(BiometricSensor.STATE_STOPPED);
+        assertThat(session.getState()).isEqualTo(STATE_ERROR_PENDING_SYSUI);
+
+        session.onDialogAnimatedIn(true);
+
+        assertThat(session.getState()).isEqualTo(STATE_AUTH_STARTED_UI_SHOWING);
+        assertThat(fingerprintSensor.getSensorState()).isEqualTo(
+                BiometricSensor.STATE_AUTHENTICATING);
+    }
+
+    @Test
     public void testCancelReducesAppetiteForCookies() throws Exception {
         setupFace(0 /* id */, false /* confirmationAlwaysRequired */,
                 mock(IBiometricAuthenticator.class));
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index d87b8d1..b5ba322 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -773,6 +773,30 @@
     }
 
     @Test
+    public void getAllPersistentDeviceIds_respectsCurrentAssociations() {
+        mVdms.onCdmAssociationsChanged(List.of(mAssociationInfo));
+        TestableLooper.get(this).processAllMessages();
+
+        assertThat(mLocalService.getAllPersistentDeviceIds())
+                .containsExactly(mDeviceImpl.getPersistentDeviceId());
+
+        mVdms.onCdmAssociationsChanged(List.of(
+                createAssociationInfo(2, AssociationRequest.DEVICE_PROFILE_APP_STREAMING),
+                createAssociationInfo(3, AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION),
+                createAssociationInfo(4, AssociationRequest.DEVICE_PROFILE_WATCH)));
+        TestableLooper.get(this).processAllMessages();
+
+        assertThat(mLocalService.getAllPersistentDeviceIds()).containsExactly(
+                VirtualDeviceImpl.createPersistentDeviceId(2),
+                VirtualDeviceImpl.createPersistentDeviceId(3));
+
+        mVdms.onCdmAssociationsChanged(Collections.emptyList());
+        TestableLooper.get(this).processAllMessages();
+
+        assertThat(mLocalService.getAllPersistentDeviceIds()).isEmpty();
+    }
+
+    @Test
     public void onAppsOnVirtualDeviceChanged_singleVirtualDevice_listenersNotified() {
         ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
         mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
diff --git a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
index 1e73a45..5aef7a3 100644
--- a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
@@ -90,7 +90,7 @@
 
     @Test
     public void getDeviceRoute_noSelectedRoutes_returnsDefaultDevice() {
-        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        MediaRoute2Info route2Info = mController.getSelectedRoute();
 
         assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DEFAULT);
         assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
@@ -105,7 +105,7 @@
         audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
         callAudioRoutesObserver(audioRoutesInfo);
 
-        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        MediaRoute2Info route2Info = mController.getSelectedRoute();
         assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
         assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
     }
@@ -117,7 +117,7 @@
 
         mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
 
-        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        MediaRoute2Info route2Info = mController.getSelectedRoute();
         assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
         assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
     }
@@ -135,7 +135,7 @@
 
         mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
 
-        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        MediaRoute2Info route2Info = mController.getSelectedRoute();
         assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
         assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
     }
@@ -155,7 +155,7 @@
 
         mController.selectRoute(null);
 
-        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        MediaRoute2Info route2Info = mController.getSelectedRoute();
         assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
         assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
     }
@@ -171,7 +171,7 @@
 
         mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
 
-        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        MediaRoute2Info route2Info = mController.getSelectedRoute();
         assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
         assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
     }
@@ -202,7 +202,7 @@
 
         mController.updateVolume(VOLUME_SAMPLE_1);
 
-        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        MediaRoute2Info route2Info = mController.getSelectedRoute();
         assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
         assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
     }
@@ -222,7 +222,7 @@
 
         mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
 
-        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        MediaRoute2Info route2Info = mController.getSelectedRoute();
         assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
         assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
     }
diff --git a/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
index 24e4851..aed68a5 100644
--- a/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
@@ -104,7 +104,7 @@
                     mOnDeviceRouteChangedListener
             );
 
-            MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute();
+            MediaRoute2Info actualMediaRoute = deviceRouteController.getSelectedRoute();
 
             assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
             assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME))
@@ -129,7 +129,7 @@
                     mOnDeviceRouteChangedListener
             );
 
-            MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute();
+            MediaRoute2Info actualMediaRoute = deviceRouteController.getSelectedRoute();
 
             assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
             assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME))
@@ -243,7 +243,7 @@
                     mOnDeviceRouteChangedListener
             );
 
-            MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute();
+            MediaRoute2Info actualMediaRoute = deviceRouteController.getSelectedRoute();
 
             assertThat(actualMediaRoute.getType()).isEqualTo(mExpectedRouteType);
             assertThat(TextUtils.equals(actualMediaRoute.getName(), mExpectedRouteNameValue))
@@ -310,7 +310,7 @@
             // Simulating wired device being connected.
             callAudioRoutesObserver(audioRoutesInfo);
 
-            MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+            MediaRoute2Info actualMediaRoute = mDeviceRouteController.getSelectedRoute();
 
             assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
             assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_HEADPHONES_NAME))
@@ -324,7 +324,7 @@
             AudioRoutesInfo fakeBluetoothAudioRoute = createFakeBluetoothAudioRoute();
             callAudioRoutesObserver(fakeBluetoothAudioRoute);
 
-            MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+            MediaRoute2Info actualMediaRoute = mDeviceRouteController.getSelectedRoute();
 
             assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
             assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME))
@@ -334,12 +334,12 @@
 
         @Test
         public void updateVolume_differentValue_updatesDeviceRouteVolume() {
-            MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+            MediaRoute2Info actualMediaRoute = mDeviceRouteController.getSelectedRoute();
             assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE);
 
             assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isTrue();
 
-            actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+            actualMediaRoute = mDeviceRouteController.getSelectedRoute();
             assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_VALUE_SAMPLE_1);
         }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
index 4b6183d..8bc027d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
@@ -31,6 +31,7 @@
 import android.app.Notification;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -39,6 +40,7 @@
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -55,6 +57,9 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ArchiveTest extends UiServiceTestCase {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private static final int SIZE = 5;
 
     private NotificationManagerService.Archive mArchive;
@@ -249,4 +254,29 @@
             assertThat(expected).contains(sbn.getKey());
         }
     }
+
+    @Test
+    public void testRemoveNotificationsByPackage() {
+        List<String> expected = new ArrayList<>();
+
+        StatusBarNotification sbn_remove = getNotification("pkg_remove", 0,
+                UserHandle.of(USER_CURRENT));
+        mArchive.record(sbn_remove, REASON_CANCEL);
+
+        StatusBarNotification sbn_keep = getNotification("pkg_keep", 1,
+                UserHandle.of(USER_CURRENT));
+        mArchive.record(sbn_keep, REASON_CANCEL);
+        expected.add(sbn_keep.getKey());
+
+        StatusBarNotification sbn_remove2 = getNotification("pkg_remove", 2,
+                UserHandle.of(USER_CURRENT));
+        mArchive.record(sbn_remove2, REASON_CANCEL);
+
+        mArchive.removePackageNotifications("pkg_remove", USER_CURRENT);
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true));
+        assertThat(actual).hasSize(expected.size());
+        for (StatusBarNotification sbn : actual) {
+            assertThat(expected).contains(sbn.getKey());
+        }
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 7fb8b30..b45dcd4 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -65,6 +65,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Build.VERSION_CODES.O_MR1;
 import static android.os.Build.VERSION_CODES.P;
+import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
 import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
 import static android.os.PowerWhitelistManager.REASON_NOTIFICATION_SERVICE;
 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
@@ -72,7 +73,6 @@
 import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
 import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
 import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
-import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
 import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
@@ -87,8 +87,6 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE;
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
@@ -309,7 +307,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.function.Consumer;
 
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@@ -739,9 +736,6 @@
         clearInvocations(mRankingHandler);
         when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
 
-        mTestFlagResolver.setFlagOverride(FSI_FORCE_DEMOTE, false);
-        mTestFlagResolver.setFlagOverride(SHOW_STICKY_HUN_FOR_DENIED_FSI, false);
-
         var checker = mock(TestableNotificationManagerService.ComponentPermissionChecker.class);
         mService.permissionChecker = checker;
         when(checker.check(anyString(), anyInt(), anyInt(), anyBoolean()))
@@ -818,6 +812,20 @@
         mPackageIntentReceiver.onReceive(getContext(), intent);
     }
 
+    private void simulatePackageRemovedBroadcast(String pkg, int uid) {
+        // mimics receive broadcast that package is removed, but doesn't remove the package.
+        final Bundle extras = new Bundle();
+        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
+                new String[]{pkg});
+        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, new int[]{uid});
+
+        final Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
+        intent.setData(Uri.parse("package:" + pkg));
+        intent.putExtras(extras);
+
+        mPackageIntentReceiver.onReceive(getContext(), intent);
+    }
+
     private void simulatePackageDistractionBroadcast(int flag, String[] pkgs, int[] uids) {
         // mimics receive broadcast that package is (un)distracting
         // but does not actually register that info with packagemanager
@@ -883,6 +891,22 @@
         mTestNotificationChannel.setAllowBubbles(channelEnabled);
     }
 
+    private void setUpPrefsForHistory(int uid, boolean globalEnabled) {
+        // Sets NOTIFICATION_HISTORY_ENABLED setting for calling process uid
+        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, globalEnabled ? 1 : 0, uid);
+        // Sets NOTIFICATION_HISTORY_ENABLED setting for uid 0
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, globalEnabled ? 1 : 0);
+
+        // Forces an update by calling observe on mSettingsObserver, which picks up the settings
+        // changes above.
+        mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper);
+
+        assertEquals(globalEnabled, Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0 /* =def */, uid) != 0);
+    }
+
     private StatusBarNotification generateSbn(String pkg, int uid, long postTime, int userId) {
         Notification.Builder nb = new Notification.Builder(mContext, "a")
                 .setContentTitle("foo")
@@ -9836,6 +9860,43 @@
     }
 
     @Test
+    public void testHandleOnPackageRemoved_ClearsHistory() throws RemoteException {
+        // Enables Notification History setting
+        setUpPrefsForHistory(mUid, true /* =enabled */);
+
+        // Posts a notification to the mTestNotificationChannel.
+        final NotificationRecord notif = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, false);
+        mService.addNotification(notif);
+        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(
+                notif.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+
+        // Cancels all notifications.
+        mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0,
+                notif.getUserId(), REASON_CANCEL);
+        waitForIdle();
+        notifs = mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+
+        // Checks that notification history's recently canceled archive contains the notification.
+        notifs = mBinderService.getHistoricalNotificationsWithAttribution(PKG,
+                        mContext.getAttributionTag(), 5 /* count */, false /* includeSnoozed */);
+        waitForIdle();
+        assertEquals(1, notifs.length);
+
+        // Remove sthe package that contained the channel
+        simulatePackageRemovedBroadcast(PKG, mUid);
+        waitForIdle();
+
+        // Checks that notification history no longer contains the notification.
+        notifs = mBinderService.getHistoricalNotificationsWithAttribution(
+                PKG, mContext.getAttributionTag(), 5 /* count */, false /* includeSnoozed */);
+        waitForIdle();
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
     public void testNotificationHistory_addNoisyNotification() throws Exception {
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
                 null /* tvExtender */);
@@ -11472,14 +11533,12 @@
         verify(mMockNm, never()).notify(anyString(), anyInt(), any(Notification.class));
     }
 
-    private void verifyStickyHun(Flag flag, int permissionState, boolean appRequested,
+    private void verifyStickyHun(int permissionState, boolean appRequested,
             boolean isSticky) throws Exception {
 
         when(mPermissionHelper.hasRequestedPermission(Manifest.permission.USE_FULL_SCREEN_INTENT,
                 PKG, mUserId)).thenReturn(appRequested);
 
-        mTestFlagResolver.setFlagOverride(flag, true);
-
         when(mPermissionManager.checkPermissionForDataDelivery(
                 eq(Manifest.permission.USE_FULL_SCREEN_INTENT), any(), any()))
                 .thenReturn(permissionState);
@@ -11503,8 +11562,7 @@
     public void testFixNotification_flagEnableStickyHun_fsiPermissionHardDenied_showStickyHun()
             throws Exception {
 
-        verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI,
-                /* permissionState= */ PermissionManager.PERMISSION_HARD_DENIED, true,
+        verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_HARD_DENIED, true,
                 /* isSticky= */ true);
     }
 
@@ -11512,16 +11570,14 @@
     public void testFixNotification_flagEnableStickyHun_fsiPermissionSoftDenied_showStickyHun()
             throws Exception {
 
-        verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI,
-                /* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, true,
+        verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, true,
                 /* isSticky= */ true);
     }
 
     @Test
     public void testFixNotification_fsiPermissionSoftDenied_appNotRequest_noShowStickyHun()
             throws Exception {
-        verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI,
-                /* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, false,
+        verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, false,
                 /* isSticky= */ false);
     }
 
@@ -11530,39 +11586,11 @@
     public void testFixNotification_flagEnableStickyHun_fsiPermissionGranted_showFsi()
             throws Exception {
 
-        verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI,
-                /* permissionState= */ PermissionManager.PERMISSION_GRANTED, true,
+        verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_GRANTED, true,
                 /* isSticky= */ false);
     }
 
     @Test
-    public void testFixNotification_flagForceStickyHun_fsiPermissionHardDenied_showStickyHun()
-            throws Exception {
-
-        verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE,
-                /* permissionState= */ PermissionManager.PERMISSION_HARD_DENIED, true,
-                /* isSticky= */ true);
-    }
-
-    @Test
-    public void testFixNotification_flagForceStickyHun_fsiPermissionSoftDenied_showStickyHun()
-            throws Exception {
-
-        verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE,
-                /* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, true,
-                /* isSticky= */ true);
-    }
-
-    @Test
-    public void testFixNotification_flagForceStickyHun_fsiPermissionGranted_showStickyHun()
-            throws Exception {
-
-        verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE,
-                /* permissionState= */ PermissionManager.PERMISSION_GRANTED, true,
-                /* isSticky= */ true);
-    }
-
-    @Test
     public void fixNotification_withFgsFlag_butIsNotFgs() throws Exception {
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
index 5147a08..29848d0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -171,19 +171,8 @@
     }
 
     @Test
-    public void testGetFsiState_stickyHunFlagDisabled_zero() {
-        final int fsiState = NotificationRecordLogger.getFsiState(
-                /* isStickyHunFlagEnabled= */ false,
-                /* hasFullScreenIntent= */ true,
-                /* hasFsiRequestedButDeniedFlag= */ true,
-                /* eventType= */ NOTIFICATION_POSTED);
-        assertEquals(0, fsiState);
-    }
-
-    @Test
     public void testGetFsiState_isUpdate_zero() {
         final int fsiState = NotificationRecordLogger.getFsiState(
-                /* isStickyHunFlagEnabled= */ true,
                 /* hasFullScreenIntent= */ true,
                 /* hasFsiRequestedButDeniedFlag= */ true,
                 /* eventType= */ NOTIFICATION_UPDATED);
@@ -193,7 +182,6 @@
     @Test
     public void testGetFsiState_hasFsi_allowedEnum() {
         final int fsiState = NotificationRecordLogger.getFsiState(
-                /* isStickyHunFlagEnabled= */ true,
                 /* hasFullScreenIntent= */ true,
                 /* hasFsiRequestedButDeniedFlag= */ false,
                 /* eventType= */ NOTIFICATION_POSTED);
@@ -203,7 +191,6 @@
     @Test
     public void testGetFsiState_fsiPermissionDenied_deniedEnum() {
         final int fsiState = NotificationRecordLogger.getFsiState(
-                /* isStickyHunFlagEnabled= */ true,
                 /* hasFullScreenIntent= */ false,
                 /* hasFsiRequestedButDeniedFlag= */ true,
                 /* eventType= */ NOTIFICATION_POSTED);
@@ -213,7 +200,6 @@
     @Test
     public void testGetFsiState_noFsi_noFsiEnum() {
         final int fsiState = NotificationRecordLogger.getFsiState(
-                /* isStickyHunFlagEnabled= */ true,
                 /* hasFullScreenIntent= */ false,
                 /* hasFsiRequestedButDeniedFlag= */ false,
                 /* eventType= */ NOTIFICATION_POSTED);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index c156e37..fe21103 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -5733,17 +5733,9 @@
     }
 
     @Test
-    public void testGetFsiState_flagDisabled_zero() {
-        final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0,
-                /* requestedFsiPermission */ true, /* isFlagEnabled= */ false);
-
-        assertEquals(0, fsiState);
-    }
-
-    @Test
     public void testGetFsiState_appDidNotRequest_enumNotRequested() {
         final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0,
-                /* requestedFsiPermission */ false, /* isFlagEnabled= */ true);
+                /* requestedFsiPermission */ false);
 
         assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED, fsiState);
     }
@@ -5754,7 +5746,7 @@
                 .thenReturn(PermissionManager.PERMISSION_GRANTED);
 
         final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0,
-                /* requestedFsiPermission= */ true, /* isFlagEnabled= */ true);
+                /* requestedFsiPermission= */ true);
 
         assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, fsiState);
     }
@@ -5765,7 +5757,7 @@
                 .thenReturn(PermissionManager.PERMISSION_SOFT_DENIED);
 
         final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0,
-                /* requestedFsiPermission = */ true, /* isFlagEnabled= */ true);
+                /* requestedFsiPermission = */ true);
 
         assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED, fsiState);
     }
@@ -5776,7 +5768,7 @@
                 .thenReturn(PermissionManager.PERMISSION_HARD_DENIED);
 
         final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0,
-                /* requestedFsiPermission = */ true, /* isFlagEnabled= */ true);
+                /* requestedFsiPermission = */ true);
 
         assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED, fsiState);
     }
@@ -5785,18 +5777,7 @@
     public void testIsFsiPermissionUserSet_appDidNotRequest_false() {
         final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0,
                 /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED,
-                /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET,
-                /* isStickyHunFlagEnabled= */ true);
-
-        assertFalse(isUserSet);
-    }
-
-    @Test
-    public void testIsFsiPermissionUserSet_flagDisabled_false() {
-        final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0,
-                /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED,
-                /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET,
-                /* isStickyHunFlagEnabled= */ false);
+                /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET);
 
         assertFalse(isUserSet);
     }
@@ -5805,8 +5786,7 @@
     public void testIsFsiPermissionUserSet_userSet_true() {
         final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0,
                 /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED,
-                /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET,
-                /* isStickyHunFlagEnabled= */ true);
+                /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET);
 
         assertTrue(isUserSet);
     }
@@ -5815,8 +5795,7 @@
     public void testIsFsiPermissionUserSet_notUserSet_false() {
         final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0,
                 /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED,
-                /* currentPermissionFlags= */ ~PackageManager.FLAG_PERMISSION_USER_SET,
-                /* isStickyHunFlagEnabled= */ true);
+                /* currentPermissionFlags= */ ~PackageManager.FLAG_PERMISSION_USER_SET);
 
         assertFalse(isUserSet);
     }
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index 1b8d746..e83f03d 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -99,4 +99,7 @@
         enabled: false,
     },
 
+    data: [
+        ":OverlayTestApp",
+    ],
 }
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 762e23c..f2a1fe8 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -119,6 +119,9 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="com.android.server.wm.ActivityRecordInputSinkTests$TestActivity"
+                  android:exported="true">
+        </activity>
     </application>
 
     <instrumentation
diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml
index 2717ef90..f8ebead 100644
--- a/services/tests/wmtests/AndroidTest.xml
+++ b/services/tests/wmtests/AndroidTest.xml
@@ -21,6 +21,7 @@
         <option name="cleanup-apks" value="true" />
         <option name="install-arg" value="-t" />
         <option name="test-file-name" value="WmTests.apk" />
+        <option name="test-file-name" value="OverlayTestApp.apk" />
     </target_preparer>
 
     <option name="test-tag" value="WmTests" />
diff --git a/services/tests/wmtests/OverlayApp/Android.bp b/services/tests/wmtests/OverlayApp/Android.bp
new file mode 100644
index 0000000..77d5b22
--- /dev/null
+++ b/services/tests/wmtests/OverlayApp/Android.bp
@@ -0,0 +1,19 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+    name: "OverlayTestApp",
+
+    srcs: ["**/*.java"],
+
+    resource_dirs: ["res"],
+
+    certificate: "platform",
+    platform_apis: true,
+}
diff --git a/services/tests/wmtests/OverlayApp/AndroidManifest.xml b/services/tests/wmtests/OverlayApp/AndroidManifest.xml
new file mode 100644
index 0000000..5b4ef57
--- /dev/null
+++ b/services/tests/wmtests/OverlayApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.server.wm.overlay_app">
+    <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
+
+    <application>
+        <activity android:name=".OverlayApp"
+                  android:exported="true"
+                  android:theme="@style/TranslucentFloatingTheme">
+        </activity>
+    </application>
+</manifest>
diff --git a/services/tests/wmtests/OverlayApp/res/values/styles.xml b/services/tests/wmtests/OverlayApp/res/values/styles.xml
new file mode 100644
index 0000000..fff10a3
--- /dev/null
+++ b/services/tests/wmtests/OverlayApp/res/values/styles.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <style name="TranslucentFloatingTheme" >
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowNoTitle">true</item>
+
+        <!-- Disables starting window. -->
+        <item name="android:windowDisablePreview">true</item>
+    </style>
+</resources>
diff --git a/services/tests/wmtests/OverlayApp/src/com/android/server/wm/overlay_app/OverlayApp.java b/services/tests/wmtests/OverlayApp/src/com/android/server/wm/overlay_app/OverlayApp.java
new file mode 100644
index 0000000..89161c5
--- /dev/null
+++ b/services/tests/wmtests/OverlayApp/src/com/android/server/wm/overlay_app/OverlayApp.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.overlay_app;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+
+/**
+ * Test app that is translucent not touchable modal.
+ * If launched with "disableInputSink" extra boolean value, this activity disables
+ * ActivityRecordInputSinkEnabled as long as the permission is granted.
+ */
+public class OverlayApp extends Activity {
+    private static final String KEY_DISABLE_INPUT_SINK = "disableInputSink";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        LinearLayout tv = new LinearLayout(this);
+        tv.setBackgroundColor(Color.GREEN);
+        tv.setPadding(50, 50, 50, 50);
+        tv.setGravity(Gravity.CENTER);
+        setContentView(tv);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+
+        if (getIntent().getBooleanExtra(KEY_DISABLE_INPUT_SINK, false)) {
+            setActivityRecordInputSinkEnabled(false);
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java
new file mode 100644
index 0000000..3b280d9
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.UiAutomation;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.MotionEvent;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.window.WindowInfosListenerForTest;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.window.flags.Flags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Internal variant of {@link android.server.wm.window.ActivityRecordInputSinkTests}.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ActivityRecordInputSinkTests {
+    private static final String OVERLAY_APP_PKG = "com.android.server.wm.overlay_app";
+    private static final String OVERLAY_ACTIVITY = OVERLAY_APP_PKG + "/.OverlayApp";
+    private static final String KEY_DISABLE_INPUT_SINK = "disableInputSink";
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @Rule
+    public final ActivityScenarioRule<TestActivity> mActivityRule =
+            new ActivityScenarioRule<>(TestActivity.class);
+
+    private UiAutomation mUiAutomation;
+
+    @Before
+    public void setUp() {
+        mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+    }
+
+    @After
+    public void tearDown() {
+        ActivityManager am =
+                InstrumentationRegistry.getInstrumentation().getContext().getSystemService(
+                        ActivityManager.class);
+        mUiAutomation.adoptShellPermissionIdentity();
+        try {
+            am.forceStopPackage(OVERLAY_APP_PKG);
+        } finally {
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    @Test
+    public void testSimpleButtonPress() {
+        injectTapOnButton();
+
+        mActivityRule.getScenario().onActivity(a -> {
+            assertEquals(1, a.mNumClicked);
+        });
+    }
+
+    @Test
+    public void testSimpleButtonPress_withOverlay() throws InterruptedException {
+        startOverlayApp(false);
+        waitForOverlayApp();
+
+        injectTapOnButton();
+
+        mActivityRule.getScenario().onActivity(a -> {
+            assertEquals(0, a.mNumClicked);
+        });
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ALLOW_DISABLE_ACTIVITY_RECORD_INPUT_SINK)
+    public void testSimpleButtonPress_withOverlayDisableInputSink() throws InterruptedException {
+        startOverlayApp(true);
+        waitForOverlayApp();
+
+        injectTapOnButton();
+
+        mActivityRule.getScenario().onActivity(a -> {
+            assertEquals(1, a.mNumClicked);
+        });
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_ALLOW_DISABLE_ACTIVITY_RECORD_INPUT_SINK)
+    public void testSimpleButtonPress_withOverlayDisableInputSink_flagDisabled()
+            throws InterruptedException {
+        startOverlayApp(true);
+        waitForOverlayApp();
+
+        injectTapOnButton();
+
+        mActivityRule.getScenario().onActivity(a -> {
+            assertEquals(0, a.mNumClicked);
+        });
+    }
+
+    private void startOverlayApp(boolean disableInputSink) {
+        String launchCommand = "am start -n " + OVERLAY_ACTIVITY;
+        if (disableInputSink) {
+            launchCommand += " --ez " + KEY_DISABLE_INPUT_SINK + " true";
+        }
+
+        mUiAutomation.adoptShellPermissionIdentity();
+        try {
+            mUiAutomation.executeShellCommand(launchCommand);
+        } finally {
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    private void waitForOverlayApp() throws InterruptedException {
+        final var listenerHost = new WindowInfosListenerForTest();
+        final var latch = new CountDownLatch(1);
+        final Consumer<List<WindowInfosListenerForTest.WindowInfo>> listener = windowInfos -> {
+            final boolean inputSinkReady = windowInfos.stream().anyMatch(info ->
+                    info.isVisible
+                            && info.name.contains("ActivityRecordInputSink " + OVERLAY_ACTIVITY));
+            if (inputSinkReady) {
+                latch.countDown();
+            }
+        };
+
+        listenerHost.addWindowInfosListener(listener);
+        try {
+            assertTrue(latch.await(5, TimeUnit.SECONDS));
+        } finally {
+            listenerHost.removeWindowInfosListener(listener);
+        }
+    }
+
+    private void injectTapOnButton() {
+        Rect buttonBounds = new Rect();
+        mActivityRule.getScenario().onActivity(a -> {
+            a.mButton.getBoundsOnScreen(buttonBounds);
+        });
+        final int x = buttonBounds.centerX();
+        final int y = buttonBounds.centerY();
+
+        MotionEvent down = MotionEvent.obtain(SystemClock.uptimeMillis(),
+                SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0);
+        mUiAutomation.injectInputEvent(down, true);
+
+        SystemClock.sleep(10);
+
+        MotionEvent up = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
+                MotionEvent.ACTION_UP, x, y, 0);
+        mUiAutomation.injectInputEvent(up, true);
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    public static class TestActivity extends Activity {
+        int mNumClicked = 0;
+        Button mButton;
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            mButton = new Button(this);
+            mButton.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.MATCH_PARENT));
+            setContentView(mButton);
+            mButton.setOnClickListener(v -> mNumClicked++);
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index e152feb..e31ee11 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -306,7 +306,7 @@
 
     @Test
     public void testCachedStateConfigurationChange() throws RemoteException {
-        doNothing().when(mClientLifecycleManager).scheduleTransactionItem(any(), any());
+        doNothing().when(mClientLifecycleManager).scheduleTransactionItemUnlocked(any(), any());
         final IApplicationThread thread = mWpc.getThread();
         final Configuration newConfig = new Configuration(mWpc.getConfiguration());
         newConfig.densityDpi += 100;
@@ -322,18 +322,17 @@
         newConfig.densityDpi += 100;
         mWpc.onConfigurationChanged(newConfig);
         verify(mClientLifecycleManager, never()).scheduleTransactionItem(eq(thread), any());
+        verify(mClientLifecycleManager, never()).scheduleTransactionItemUnlocked(eq(thread), any());
 
         // Cached -> non-cached will send the previous deferred config immediately.
         mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER);
         final ArgumentCaptor<ConfigurationChangeItem> captor =
                 ArgumentCaptor.forClass(ConfigurationChangeItem.class);
-        verify(mClientLifecycleManager).scheduleTransactionItem(eq(thread), captor.capture());
+        verify(mClientLifecycleManager).scheduleTransactionItemUnlocked(
+                eq(thread), captor.capture());
         final ClientTransactionHandler client = mock(ClientTransactionHandler.class);
         captor.getValue().preExecute(client);
-        final ArgumentCaptor<Configuration> configCaptor =
-                ArgumentCaptor.forClass(Configuration.class);
-        verify(client).updatePendingConfiguration(configCaptor.capture());
-        assertEquals(newConfig, configCaptor.getValue());
+        verify(client).updatePendingConfiguration(newConfig);
     }
 
     @Test
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 63de41f..4c56f33 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1953,6 +1953,13 @@
             }
         }
 
+        // Flags status.
+        pw.println("Flags:");
+        pw.println("    " + Flags.FLAG_USER_INTERACTION_TYPE_API
+                + ": " + Flags.userInteractionTypeApi());
+        pw.println("    " + Flags.FLAG_USE_PARCELED_LIST
+                + ": " + Flags.useParceledList());
+
         final int[] userIds;
         synchronized (mLock) {
             final int userCount = mUserState.size();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index a584fc9..b775963 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -2437,8 +2437,6 @@
                     synchronized (VoiceInteractionManagerServiceStub.this) {
                         Slog.i(TAG, "Force stopping current voice recognizer: "
                                 + getCurRecognizer(userHandle));
-                        // TODO: Figure out why the interactor was being cleared and document it.
-                        setCurInteractor(null, userHandle);
                         initRecognizer(userHandle);
                     }
                 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f8608b8..e12a815 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -23,6 +23,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.ColorInt;
 import android.annotation.DurationMillisLong;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -70,6 +71,7 @@
 import com.android.internal.telephony.ISetOpportunisticDataCallback;
 import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.HandlerExecutor;
 import com.android.internal.util.FunctionalUtils;
 import com.android.internal.util.Preconditions;
@@ -95,7 +97,22 @@
 import java.util.stream.Collectors;
 
 /**
- * Subscription manager provides the mobile subscription information.
+ * Subscription manager provides the mobile subscription information that are associated with the
+ * calling user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U)
+ * and below can see all subscriptions as it does today.
+ *
+ * <p>For example, if we have
+ * <ul>
+ *     <li> Subscription 1 associated with personal profile.
+ *     <li> Subscription 2 associated with work profile.
+ * </ul>
+ * Then for SDK 35+, if the caller identity is personal profile, then
+ * {@link #getActiveSubscriptionInfoList} will return subscription 1 only and vice versa.
+ *
+ * <p>If the caller needs to see all subscriptions across user profiles,
+ * use {@link #createForAllUserProfiles} to convert the instance to see all. Additional permission
+ * may be required as documented on the each API.
+ *
  */
 @SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -1446,6 +1463,16 @@
         }
     }
 
+    /**
+     * {@code true} if the SubscriptionManager instance should see all subscriptions regardless its
+     * association with particular user profile.
+     *
+     * <p> It only applies to Android SDK 35(V) and above. For Android SDK 34(U) and below, the
+     * caller can see all subscription across user profiles as it does today today even if it's
+     * {@code false}.
+     */
+    private boolean mIsForAllUserProfiles = false;
+
     /** @hide */
     @UnsupportedAppUsage
     public SubscriptionManager(Context context) {
@@ -1776,8 +1803,23 @@
     }
 
     /**
-     * Get the SubscriptionInfo(s) of the currently active SIM(s). The records will be sorted
-     * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
+     * Get the SubscriptionInfo(s) of the currently active SIM(s) associated with the current caller
+     * user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U)
+     * and below can see all subscriptions as it does today.
+     *
+     * <p>For example, if we have
+     * <ul>
+     *     <li> Subscription 1 associated with personal profile.
+     *     <li> Subscription 2 associated with work profile.
+     * </ul>
+     * Then for SDK 35+, if the caller identity is personal profile, then this will return
+     * subscription 1 only and vice versa.
+     *
+     * <p>If the caller needs to see all subscriptions across user profiles,
+     * use {@link #createForAllUserProfiles} to convert this instance to see all.
+     *
+     * <p> The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
+     * {@link SubscriptionInfo#getSubscriptionId}.
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see
@@ -1800,8 +1842,25 @@
      * </ul>
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    // @RequiresPermission(TODO(b/308809058))
     public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
-        return getActiveSubscriptionInfoList(/* userVisibleonly */true);
+        List<SubscriptionInfo> activeList = null;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
+                        mContext.getAttributionTag(), mIsForAllUserProfiles);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        if (activeList != null) {
+            activeList = activeList.stream().filter(subInfo -> isSubscriptionVisible(subInfo))
+                    .collect(Collectors.toList());
+        }
+        return activeList;
     }
 
     /**
@@ -1835,6 +1894,26 @@
     }
 
     /**
+     * Convert this subscription manager instance into one that can see all subscriptions across
+     * user profiles.
+     *
+     * @return a SubscriptionManager that can see all subscriptions regardless its user profile
+     * association.
+     *
+     * @see #getActiveSubscriptionInfoList
+     * @see #getActiveSubscriptionInfoCount
+     * @see UserHandle
+     */
+    @FlaggedApi(Flags.FLAG_WORK_PROFILE_API_SPLIT)
+    // @RequiresPermission(TODO(b/308809058))
+    // The permission check for accessing all subscriptions will be enforced upon calling the
+    // individual APIs linked above.
+    @NonNull public SubscriptionManager createForAllUserProfiles() {
+        mIsForAllUserProfiles = true;
+        return this;
+    }
+
+    /**
     * This is similar to {@link #getActiveSubscriptionInfoList()}, but if userVisibleOnly
     * is true, it will filter out the hidden subscriptions.
     *
@@ -1847,7 +1926,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
-                        mContext.getAttributionTag());
+                        mContext.getAttributionTag(), true /*isForAllUserProfiles*/);
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -2002,13 +2081,19 @@
     }
 
     /**
-     * Get the active subscription count.
+     * Get the active subscription count associated with the current caller user profile for
+     * Android SDK 35(V) and above, while Android SDK 34(U) and below can see all subscriptions as
+     * it does today.
+     *
+     * <p>If the caller needs to see all subscriptions across user profiles,
+     * use {@link #createForAllUserProfiles} to convert this instance to see all.
      *
      * @return The current number of active subscriptions.
      *
      * @see #getActiveSubscriptionInfoList()
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    // @RequiresPermission(TODO(b/308809058))
     public int getActiveSubscriptionInfoCount() {
         int result = 0;
 
@@ -2016,7 +2101,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(),
-                        mContext.getAttributionTag());
+                        mContext.getAttributionTag(), mIsForAllUserProfiles);
             }
         } catch (RemoteException ex) {
             // ignore it
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index d2dbeb7..3f41d56 100644
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -66,6 +66,8 @@
      *
      * @param callingPackage The package maing the call.
      * @param callingFeatureId The feature in the package
+     * @param isForAllProfiles whether the caller intends to see all subscriptions regardless
+     *                      association.
      * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
      * <ul>
      * <li>
@@ -83,14 +85,17 @@
      * </ul>
      */
     List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage,
-            String callingFeatureId);
+            String callingFeatureId, boolean isForAllProfiles);
 
     /**
      * @param callingPackage The package making the call.
      * @param callingFeatureId The feature in the package.
+     * @param isForAllProfile whether the caller intends to see all subscriptions regardless
+     *                      association.
      * @return the number of active subscriptions
      */
-    int getActiveSubInfoCount(String callingPackage, String callingFeatureId);
+    int getActiveSubInfoCount(String callingPackage, String callingFeatureId,
+            boolean isForAllProfile);
 
     /**
      * @return the maximum number of subscriptions this device will support at any one time.
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index b44f1a6..c49f8fe 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -23,10 +23,13 @@
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.LegacyFlickerTest
 import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.device.traces.parsers.toFlickerComponent
 import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.BaseTest
 import com.android.server.wm.flicker.helpers.ImeAppHelper
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_FINISH_ACTIVITY
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -53,7 +56,12 @@
             testApp.launchViaIntent(wmHelper)
             testApp.openIME(wmHelper)
         }
-        transitions { testApp.finishActivity(wmHelper) }
+        transitions {
+            broadcastActionTrigger.doAction(ACTION_FINISH_ACTIVITY)
+            wmHelper.StateSyncBuilder()
+                    .withActivityRemoved(ActivityOptions.Ime.Default.COMPONENT.toFlickerComponent())
+                    .waitForAndVerify()
+        }
         teardown { simpleApp.exit(wmHelper) }
     }
 
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
index 976ac82..994edc5 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -28,6 +28,7 @@
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
 import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_TOGGLE_ORIENTATION
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -53,7 +54,11 @@
             // Enable letterbox when the app calls setRequestedOrientation
             device.executeShellCommand("cmd window set-ignore-orientation-request true")
         }
-        transitions { testApp.toggleFixPortraitOrientation(wmHelper) }
+        transitions {
+            broadcastActionTrigger.doAction(ACTION_TOGGLE_ORIENTATION)
+            // Ensure app relaunching transition finished and the IME was shown
+            testApp.waitIMEShown(wmHelper)
+        }
         teardown {
             testApp.exit()
             device.executeShellCommand("cmd window set-ignore-orientation-request false")
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
index aff8e65..6ee5a9a 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -104,7 +104,7 @@
 
     @Presubmit
     @Test
-    open fun imeLayerIsVisibleWhenSwitchingToImeApp() {
+    fun imeLayerIsVisibleWhenSwitchingToImeApp() {
         flicker.assertLayersStart { isVisible(ComponentNameMatcher.IME) }
         flicker.assertLayersTag(TAG_IME_VISIBLE) { isVisible(ComponentNameMatcher.IME) }
         flicker.assertLayersEnd { isVisible(ComponentNameMatcher.IME) }
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
index 4ffdcea..1ad5c0d 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -93,7 +93,7 @@
         }
         transitions {
             testApp.launchViaIntent(wmHelper)
-            wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify()
+            testApp.waitIMEShown(wmHelper)
         }
     }
 
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index 6ad235c..181a2a2 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -23,11 +23,14 @@
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.LegacyFlickerTest
 import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.device.traces.parsers.toFlickerComponent
 import android.view.WindowInsets.Type.ime
 import android.view.WindowInsets.Type.navigationBars
 import android.view.WindowInsets.Type.statusBars
 import com.android.server.wm.flicker.BaseTest
 import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_START_DIALOG_THEMED_ACTIVITY
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.FixMethodOrder
@@ -50,8 +53,12 @@
     override val transition: FlickerBuilder.() -> Unit = {
         setup {
             testApp.launchViaIntent(wmHelper)
-            wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify()
-            testApp.startDialogThemedActivity(wmHelper)
+            testApp.waitIMEShown(wmHelper)
+            broadcastActionTrigger.doAction(ACTION_START_DIALOG_THEMED_ACTIVITY)
+            wmHelper.StateSyncBuilder()
+                    .withFullScreenApp(
+                        ActivityOptions.DialogThemedActivity.COMPONENT.toFlickerComponent())
+                    .waitForAndVerify()
             // Verify IME insets isn't visible on dialog since it's non-IME focusable window
             assertFalse(testApp.getInsetsVisibleFromDialog(ime()))
             assertTrue(testApp.getInsetsVisibleFromDialog(statusBars()))
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index 7c9c05d..ad272a0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker
 
 import android.app.Instrumentation
+import android.content.Intent
 import android.platform.test.annotations.Presubmit
 import android.tools.common.traces.component.ComponentNameMatcher
 import android.tools.device.flicker.junit.FlickerBuilderProvider
@@ -50,6 +51,19 @@
     /** Specification of the test transition to execute */
     abstract val transition: FlickerBuilder.() -> Unit
 
+    protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
+
+    // Helper class to process test actions by broadcast.
+    protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
+        private fun createIntentWithAction(broadcastAction: String): Intent {
+            return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+        }
+
+        fun doAction(broadcastAction: String) {
+            instrumentation.context.sendBroadcast(createIntentWithAction(broadcastAction))
+        }
+    }
+
     /**
      * Entry point for the test runner. It will use this method to initialize and cache flicker
      * executions
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index 252f7d3..cb1aab0 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -50,7 +50,7 @@
         waitIMEShown(wmHelper)
     }
 
-    protected fun waitIMEShown(wmHelper: WindowManagerStateHelper) {
+    fun waitIMEShown(wmHelper: WindowManagerStateHelper) {
         wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify()
     }
 
@@ -63,17 +63,4 @@
         uiDevice.pressBack()
         wmHelper.StateSyncBuilder().withImeGone().waitForAndVerify()
     }
-
-    open fun finishActivity(wmHelper: WindowManagerStateHelper) {
-        val finishButton =
-            uiDevice.wait(
-                Until.findObject(By.res(packageName, "finish_activity_btn")),
-                FIND_TIMEOUT
-            )
-        requireNotNull(finishButton) {
-            "Finish activity button not found, probably IME activity is not on the screen?"
-        }
-        finishButton.click()
-        wmHelper.StateSyncBuilder().withActivityRemoved(this).waitForAndVerify()
-    }
 }
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
index d3cee64..0ee7aee 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
@@ -74,24 +74,6 @@
         open(expectedPackage)
     }
 
-    fun startDialogThemedActivity(wmHelper: WindowManagerStateHelper) {
-        val button =
-            uiDevice.wait(
-                Until.findObject(By.res(packageName, "start_dialog_themed_activity_btn")),
-                FIND_TIMEOUT
-            )
-
-        requireNotNull(button) {
-            "Button not found, this usually happens when the device " +
-                "was left in an unknown state (e.g. Screen turned off)"
-        }
-        button.click()
-        wmHelper
-            .StateSyncBuilder()
-            .withFullScreenApp(ActivityOptions.DialogThemedActivity.COMPONENT.toFlickerComponent())
-            .waitForAndVerify()
-    }
-
     fun dismissDialog(wmHelper: WindowManagerStateHelper) {
         val dialog = uiDevice.wait(Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT)
 
@@ -126,20 +108,4 @@
         }
         return false
     }
-
-    fun toggleFixPortraitOrientation(wmHelper: WindowManagerStateHelper) {
-        val button =
-            uiDevice.wait(
-                Until.findObject(By.res(packageName, "toggle_fixed_portrait_btn")),
-                FIND_TIMEOUT
-            )
-        require(button != null) {
-            "Button not found, this usually happens when the device " +
-                "was left in an unknown state (e.g. Screen turned off)"
-        }
-        button.click()
-        instrumentation.waitForIdleSync()
-        // Ensure app relaunching transition finish and the IME has shown
-        waitIMEShown(wmHelper)
-    }
 }
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index fa73e2c..507c1b6 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
  Copyright 2018 The Android Open Source Project
 
  Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,39 +13,17 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 -->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
+    android:background="@android:color/holo_green_light"
     android:focusableInTouchMode="true"
-    android:background="@android:color/holo_green_light">
-    <EditText android:id="@+id/plain_text_input"
-              android:layout_height="wrap_content"
-              android:layout_width="match_parent"
-	      android:imeOptions="flagNoExtractUi"
-              android:inputType="text"/>
-    <LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical">
+
+    <EditText
+        android:id="@+id/plain_text_input"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="horizontal">
-        <Button
-            android:id="@+id/finish_activity_btn"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Finish activity" />
-        <Button
-            android:id="@+id/start_dialog_themed_activity_btn"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Dialog themed activity" />
-        <ToggleButton
-            android:id="@+id/toggle_fixed_portrait_btn"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textOn="Portrait (On)"
-            android:textOff="Portrait (Off)"
-        />
-    </LinearLayout>
+        android:layout_height="wrap_content"
+        android:imeOptions="flagNoExtractUi"
+        android:inputType="text" />
 </LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 8b334c0..80c1dd0 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -40,6 +40,18 @@
             public static final String LABEL = "ImeActivity";
             public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
                     FLICKER_APP_PACKAGE + ".ImeActivity");
+
+            /** Intent action used to finish the test activity. */
+            public static final String ACTION_FINISH_ACTIVITY =
+                    FLICKER_APP_PACKAGE + ".ImeActivity.FINISH_ACTIVITY";
+
+            /** Intent action used to start a {@link DialogThemedActivity}. */
+            public static final String ACTION_START_DIALOG_THEMED_ACTIVITY =
+                    FLICKER_APP_PACKAGE + ".ImeActivity.START_DIALOG_THEMED_ACTIVITY";
+
+            /** Intent action used to toggle activity orientation. */
+            public static final String ACTION_TOGGLE_ORIENTATION =
+                    FLICKER_APP_PACKAGE + ".ImeActivity.TOGGLE_ORIENTATION";
         }
 
         public static class AutoFocusActivity {
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
index d7ee2af..4418b5a 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
@@ -16,12 +16,51 @@
 
 package com.android.server.wm.flicker.testapp;
 
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_FINISH_ACTIVITY;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_START_DIALOG_THEMED_ACTIVITY;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_TOGGLE_ORIENTATION;
+
 import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.WindowManager;
-import android.widget.Button;
 
 public class ImeActivity extends Activity {
+
+    private static final String TAG = "ImeActivity";
+
+    /** Receiver used to handle actions coming from the test helper methods. */
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case ACTION_FINISH_ACTIVITY -> finish();
+                case ACTION_START_DIALOG_THEMED_ACTIVITY -> startActivity(
+                        new Intent(context, DialogThemedActivity.class));
+                case ACTION_TOGGLE_ORIENTATION -> {
+                    mIsPortrait = !mIsPortrait;
+                    setRequestedOrientation(mIsPortrait
+                            ? SCREEN_ORIENTATION_PORTRAIT
+                            : SCREEN_ORIENTATION_UNSPECIFIED);
+                }
+                default -> Log.w(TAG, "Unhandled action=" + intent.getAction());
+            }
+        }
+    };
+
+    /**
+     * Used to toggle activity orientation between portrait when {@code true} and
+     * unspecified otherwise.
+     */
+    private boolean mIsPortrait = false;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -30,9 +69,17 @@
                 .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
         getWindow().setAttributes(p);
         setContentView(R.layout.activity_ime);
-        Button button = findViewById(R.id.finish_activity_btn);
-        button.setOnClickListener(view -> {
-            finish();
-        });
+
+        final var filter = new IntentFilter();
+        filter.addAction(ACTION_FINISH_ACTIVITY);
+        filter.addAction(ACTION_START_DIALOG_THEMED_ACTIVITY);
+        filter.addAction(ACTION_TOGGLE_ORIENTATION);
+        registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
+    }
+
+    @Override
+    protected void onDestroy() {
+        unregisterReceiver(mBroadcastReceiver);
+        super.onDestroy();
     }
 }
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
index 7ee8deb..cd711f7 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
@@ -16,29 +16,12 @@
 
 package com.android.server.wm.flicker.testapp;
 
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
-import android.content.Intent;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ToggleButton;
-
 public class ImeActivityAutoFocus extends ImeActivity {
     @Override
     protected void onStart() {
         super.onStart();
 
-        Button startThemedActivityButton = findViewById(R.id.start_dialog_themed_activity_btn);
-        startThemedActivityButton.setOnClickListener(
-                button -> startActivity(new Intent(this, DialogThemedActivity.class)));
-
-        ToggleButton toggleFixedPortraitButton = findViewById(R.id.toggle_fixed_portrait_btn);
-        toggleFixedPortraitButton.setOnCheckedChangeListener(
-                (button, isChecked) -> setRequestedOrientation(
-                        isChecked ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_UNSPECIFIED));
-
-        EditText editTextField = findViewById(R.id.plain_text_input);
+        final var editTextField = findViewById(R.id.plain_text_input);
         editTextField.requestFocus();
     }
 }
diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp
index 15aaa46..83ced2c 100644
--- a/tests/InputScreenshotTest/Android.bp
+++ b/tests/InputScreenshotTest/Android.bp
@@ -7,12 +7,27 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+filegroup {
+    name: "InputScreenshotTestRNGFiles",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    exclude_srcs: [
+        "src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt",
+        "src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt",
+    ],
+}
+
 android_test {
     name: "InputScreenshotTests",
     srcs: [
         "src/**/*.java",
         "src/**/*.kt",
     ],
+    exclude_srcs: [
+        "src/android/input/screenshot/package-info.java",
+    ],
     platform_apis: true,
     certificate: "platform",
     static_libs: [
@@ -43,6 +58,7 @@
         "hamcrest-library",
         "kotlin-test",
         "flag-junit",
+        "platform-parametric-runner-lib",
         "platform-test-annotations",
         "services.core.unboosted",
         "testables",
diff --git a/tests/InputScreenshotTest/robotests/Android.bp b/tests/InputScreenshotTest/robotests/Android.bp
new file mode 100644
index 0000000..912f4b80
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/Android.bp
@@ -0,0 +1,71 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_library {
+    name: "InputRoboRNGTestsAssetsLib",
+    asset_dirs: ["assets"],
+    sdk_version: "current",
+    platform_apis: true,
+    manifest: "AndroidManifest.xml",
+    optimize: {
+        enabled: false,
+    },
+    use_resource_processor: true,
+}
+
+android_app {
+    name: "InputRoboApp",
+    srcs: [],
+    static_libs: [
+        "androidx.test.espresso.core",
+        "androidx.appcompat_appcompat",
+        "flag-junit",
+        "guava",
+        "InputRoboRNGTestsAssetsLib",
+        "platform-screenshot-diff-core",
+        "PlatformComposeSceneTransitionLayoutTestsUtils",
+    ],
+    manifest: "robo-manifest.xml",
+    aaptflags: [
+        "--extra-packages",
+        "com.android.input.screenshot",
+    ],
+    dont_merge_manifests: true,
+    platform_apis: true,
+    system_ext_specific: true,
+    certificate: "platform",
+    privileged: true,
+    resource_dirs: [],
+    kotlincflags: ["-Xjvm-default=all"],
+
+    plugins: ["dagger2-compiler"],
+    use_resource_processor: true,
+}
+
+android_robolectric_test {
+    name: "InputRoboRNGTests",
+    srcs: [
+        ":InputScreenshotTestRNGFiles",
+        ":flag-junit",
+        ":platform-test-screenshot-rules",
+    ],
+    // Do not add any new libraries here, they should be added to SystemUIGoogleRobo above.
+    static_libs: [
+        "androidx.compose.runtime_runtime",
+        "androidx.test.uiautomator_uiautomator",
+        "androidx.test.ext.junit",
+        "inline-mockito-robolectric-prebuilt",
+        "platform-parametric-runner-lib",
+        "uiautomator-helpers",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+        "truth",
+    ],
+    upstream: true,
+    java_resource_dirs: ["config"],
+    instrumentation_for: "InputRoboApp",
+}
diff --git a/tests/InputScreenshotTest/robotests/AndroidManifest.xml b/tests/InputScreenshotTest/robotests/AndroidManifest.xml
new file mode 100644
index 0000000..5689311
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.input.screenshot">
+    <uses-sdk android:minSdkVersion="21"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+</manifest>
diff --git a/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png
new file mode 100644
index 0000000..baf204a
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png
new file mode 100644
index 0000000..deb3cee
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png
new file mode 100644
index 0000000..34e25f7
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/robotests/config/robolectric.properties b/tests/InputScreenshotTest/robotests/config/robolectric.properties
new file mode 100644
index 0000000..83d7549
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/config/robolectric.properties
@@ -0,0 +1,15 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+sdk=NEWEST_SDK
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/robotests/robo-manifest.xml b/tests/InputScreenshotTest/robotests/robo-manifest.xml
new file mode 100644
index 0000000..e86f58e
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/robo-manifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--- Include all the namespaces we will ever need anywhere, because this is the source the manifest merger uses for namespaces -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.android.input.screenshot"
+          coreApp="true">
+  <application>
+    <activity
+        android:name="androidx.activity.ComponentActivity"
+        android:exported="true">
+    </activity>
+  </application>
+</manifest>
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt
index c2c3d55..75dab41 100644
--- a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.graphics.Bitmap
+import android.os.Build
 import androidx.activity.ComponentActivity
 import androidx.compose.foundation.Image
 import androidx.compose.ui.platform.ViewRootForTest
@@ -49,15 +50,17 @@
             )
         )
     private val composeRule = createAndroidComposeRule<ComponentActivity>()
-    private val delegateRule =
-            RuleChain.outerRule(colorsRule)
-                .around(deviceEmulationRule)
+    private val roboRule =
+            RuleChain.outerRule(deviceEmulationRule)
                 .around(screenshotRule)
                 .around(composeRule)
+    private val delegateRule = RuleChain.outerRule(colorsRule).around(roboRule)
     private val matcher = UnitTestBitmapMatcher
+    private val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false
 
     override fun apply(base: Statement, description: Description): Statement {
-        return delegateRule.apply(base, description)
+        val ruleToApply = if (isRobolectric) roboRule else delegateRule
+        return ruleToApply.apply(base, description)
     }
 
     /**
@@ -84,4 +87,4 @@
         val view = (composeRule.onRoot().fetchSemanticsNode().root as ViewRootForTest).view
         screenshotRule.assertBitmapAgainstGolden(view.drawIntoBitmap(), goldenIdentifier, matcher)
     }
-}
\ No newline at end of file
+}
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt
index 8ae6dfd..ab7bb4e 100644
--- a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt
@@ -26,14 +26,15 @@
 import org.junit.Test
 import org.junit.rules.RuleChain
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 import platform.test.screenshot.DeviceEmulationSpec
 
 /** A screenshot test for Keyboard layout preview for Iso physical layout. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 class KeyboardLayoutPreviewIsoScreenshotTest(emulationSpec: DeviceEmulationSpec) {
     companion object {
-        @Parameterized.Parameters(name = "{0}")
+        @Parameters(name = "{0}")
         @JvmStatic
         fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
     }
@@ -55,4 +56,4 @@
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java b/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java
new file mode 100644
index 0000000..4b5a56d
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java
@@ -0,0 +1,4 @@
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.input.screenshot;
+
+import org.robolectric.annotation.GraphicsMode;
diff --git a/tests/SmokeTestApps/Android.bp b/tests/SmokeTestApps/Android.bp
index 3505fe1..38ee8ac 100644
--- a/tests/SmokeTestApps/Android.bp
+++ b/tests/SmokeTestApps/Android.bp
@@ -11,4 +11,7 @@
     name: "SmokeTestTriggerApps",
     srcs: ["src/**/*.java"],
     sdk_version: "current",
+    errorprone: {
+        enabled: false,
+    },
 }
diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/tools/hoststubgen/hoststubgen/framework-policy-override.txt
index ff0fe32..493ad56 100644
--- a/tools/hoststubgen/hoststubgen/framework-policy-override.txt
+++ b/tools/hoststubgen/hoststubgen/framework-policy-override.txt
@@ -78,6 +78,9 @@
 class com.android.internal.util.FastPrintWriter         keepclass
 class com.android.internal.util.LineBreakBufferedWriter keepclass
 
+class android.util.EventLog stubclass
+class android.util.EventLog !com.android.hoststubgen.nativesubstitution.EventLog_host
+class android.util.EventLog$Event stubclass
 
 # Expose Context because it's referred to by AndroidTestCase, but don't need to expose any of
 # its members.
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java
new file mode 100644
index 0000000..292e8da
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.hoststubgen.nativesubstitution;
+
+import android.util.Log;
+import android.util.Log.Level;
+
+import java.util.Collection;
+
+public class EventLog_host {
+    public static int writeEvent(int tag, int value) {
+        return writeEvent(tag, (Object) value);
+    }
+
+    public static int writeEvent(int tag, long value) {
+        return writeEvent(tag, (Object) value);
+    }
+
+    public static int writeEvent(int tag, float value) {
+        return writeEvent(tag, (Object) value);
+    }
+
+    public static int writeEvent(int tag, String str) {
+        return writeEvent(tag, (Object) str);
+    }
+
+    public static int writeEvent(int tag, Object... list) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("logd: [event] ");
+        final String tagName = android.util.EventLog.getTagName(tag);
+        if (tagName != null) {
+            sb.append(tagName);
+        } else {
+            sb.append(tag);
+        }
+        sb.append(": [");
+        for (int i = 0; i < list.length; i++) {
+            sb.append(String.valueOf(list[i]));
+            if (i < list.length - 1) {
+                sb.append(',');
+            }
+        }
+        sb.append(']');
+        System.out.println(sb.toString());
+        return sb.length();
+    }
+
+    public static void readEvents(int[] tags, Collection<android.util.EventLog.Event> output) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static void readEventsOnWrapping(int[] tags, long timestamp,
+            Collection<android.util.EventLog.Event> output) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
index 4a3a798..668c94c 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
@@ -27,9 +27,10 @@
 
 /**
  * Tentative, partial implementation of the Parcel native methods, using Java's
- * {@link ByteBuffer}. It turned out there's enough semantics differences between Parcel
- * and {@link ByteBuffer}, so it didn't actually work.
- * (e.g. Parcel seems to allow moving the data position to be beyond its size? Which
+ * {@code byte[]}.
+ * (We don't use a {@link ByteBuffer} because there's enough semantics differences between Parcel
+ * and {@link ByteBuffer}, and it didn't work out.
+ * e.g. Parcel seems to allow moving the data position to be beyond its size? Which
  * {@link ByteBuffer} wouldn't allow...)
  */
 public class Parcel_host {
diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
index 89daa20..85038be 100755
--- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
+++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
@@ -13,6 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# This command is expected to be executed with: atest hoststubgen-invoke-test
+
 set -e # Exit when any command files
 
 # This script runs HostStubGen directly with various arguments and make sure
@@ -35,6 +37,12 @@
   mkdir -p $TEMP
 fi
 
+cleanup_temp() {
+  rm -fr $TEMP/*
+}
+
+cleanup_temp
+
 JAR=hoststubgen-test-tiny-framework.jar
 STUB=$TEMP/stub.jar
 IMPL=$TEMP/impl.jar
@@ -47,12 +55,10 @@
 # HostStubGen result in it.
 HOSTSTUBGEN_RC=0
 
-# Define the functions to
-
-
 # Note, because the build rule will only install hoststubgen.jar, but not the wrapper script,
 # we need to execute it manually with the java command.
 hoststubgen() {
+  echo "Running hoststubgen with: $*"
   java -jar ./hoststubgen.jar "$@"
 }
 
@@ -62,7 +68,7 @@
 
   echo "# Test: $test_name"
 
-  rm -f $HOSTSTUBGEN_OUT
+  cleanup_temp
 
   local filter_arg=""
 
@@ -73,11 +79,21 @@
     cat $ANNOTATION_FILTER
   fi
 
+  local stub_arg=""
+  local impl_arg=""
+
+  if [[ "$STUB" != "" ]] ; then
+    stub_arg="--out-stub-jar $STUB"
+  fi
+  if [[ "$IMPL" != "" ]] ; then
+    impl_arg="--out-impl-jar $IMPL"
+  fi
+
   hoststubgen \
       --debug \
       --in-jar $JAR \
-      --out-stub-jar $STUB \
-      --out-impl-jar $IMPL \
+      $stub_arg \
+      $impl_arg \
       --stub-annotation \
           android.hosttest.annotation.HostSideTestStub \
       --keep-annotation \
@@ -105,6 +121,21 @@
   return 0
 }
 
+assert_file_generated() {
+  local file="$1"
+  if [[ "$file" == "" ]] ; then
+    if [[ -f "$file" ]] ; then
+      echo "HostStubGen shouldn't have generated $file"
+      return 1
+    fi
+  else
+    if ! [[ -f "$file" ]] ; then
+      echo "HostStubGen didn't generate $file"
+      return 1
+    fi
+  fi
+}
+
 run_hoststubgen_for_success() {
   run_hoststubgen "$@"
 
@@ -112,6 +143,9 @@
     echo "HostStubGen expected to finish successfully, but failed with $rc"
     return 1
   fi
+
+  assert_file_generated "$STUB"
+  assert_file_generated "$IMPL"
 }
 
 run_hoststubgen_for_failure() {
@@ -189,6 +223,11 @@
 * # All other classes allowed
 "
 
+STUB="" run_hoststubgen_for_success "No stub generation" ""
+
+IMPL="" run_hoststubgen_for_success "No impl generation" ""
+
+STUB="" IMPL="" run_hoststubgen_for_success "No stub, no impl generation" ""
 
 
 echo "All tests passed"
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 07bd2dc..3cdddc2 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -237,8 +237,8 @@
      */
     private fun convert(
             inJar: String,
-            outStubJar: String,
-            outImplJar: String,
+            outStubJar: String?,
+            outImplJar: String?,
             filter: OutputFilter,
             enableChecker: Boolean,
             classes: ClassNodes,
@@ -254,8 +254,8 @@
         log.withIndent {
             // Open the input jar file and process each entry.
             ZipFile(inJar).use { inZip ->
-                ZipOutputStream(FileOutputStream(outStubJar)).use { stubOutStream ->
-                    ZipOutputStream(FileOutputStream(outImplJar)).use { implOutStream ->
+                maybeWithZipOutputStream(outStubJar) { stubOutStream ->
+                    maybeWithZipOutputStream(outImplJar) { implOutStream ->
                         val inEntries = inZip.entries()
                         while (inEntries.hasMoreElements()) {
                             val entry = inEntries.nextElement()
@@ -265,22 +265,29 @@
                         log.i("Converted all entries.")
                     }
                 }
-                log.i("Created stub: $outStubJar")
-                log.i("Created impl: $outImplJar")
+                outStubJar?.let { log.i("Created stub: $it") }
+                outImplJar?.let { log.i("Created impl: $it") }
             }
         }
         val end = System.currentTimeMillis()
         log.v("Done transforming the jar in %.1f second(s).", (end - start) / 1000.0)
     }
 
+    private fun <T> maybeWithZipOutputStream(filename: String?, block: (ZipOutputStream?) -> T): T {
+        if (filename == null) {
+            return block(null)
+        }
+        return ZipOutputStream(FileOutputStream(filename)).use(block)
+    }
+
     /**
      * Convert a single ZIP entry, which may or may not be a class file.
      */
     private fun convertSingleEntry(
             inZip: ZipFile,
             entry: ZipEntry,
-            stubOutStream: ZipOutputStream,
-            implOutStream: ZipOutputStream,
+            stubOutStream: ZipOutputStream?,
+            implOutStream: ZipOutputStream?,
             filter: OutputFilter,
             packageRedirector: PackageRedirectRemapper,
             enableChecker: Boolean,
@@ -316,8 +323,8 @@
             // Unknown type, we just copy it to both output zip files.
             // TODO: We probably shouldn't do it for stub jar?
             log.v("Copying: %s", entry.name)
-            copyZipEntry(inZip, entry, stubOutStream)
-            copyZipEntry(inZip, entry, implOutStream)
+            stubOutStream?.let { copyZipEntry(inZip, entry, it) }
+            implOutStream?.let { copyZipEntry(inZip, entry, it) }
         }
     }
 
@@ -346,8 +353,8 @@
     private fun processSingleClass(
             inZip: ZipFile,
             entry: ZipEntry,
-            stubOutStream: ZipOutputStream,
-            implOutStream: ZipOutputStream,
+            stubOutStream: ZipOutputStream?,
+            implOutStream: ZipOutputStream?,
             filter: OutputFilter,
             packageRedirector: PackageRedirectRemapper,
             enableChecker: Boolean,
@@ -361,7 +368,7 @@
             return
         }
         // Generate stub first.
-        if (classPolicy.policy.needsInStub) {
+        if (stubOutStream != null && classPolicy.policy.needsInStub) {
             log.v("Creating stub class: %s Policy: %s", classInternalName, classPolicy)
             log.withIndent {
                 BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
@@ -374,8 +381,8 @@
                 }
             }
         }
-        log.v("Creating impl class: %s Policy: %s", classInternalName, classPolicy)
-        if (classPolicy.policy.needsInImpl) {
+        if (implOutStream != null && classPolicy.policy.needsInImpl) {
+            log.v("Creating impl class: %s Policy: %s", classInternalName, classPolicy)
             log.withIndent {
                 BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
                     val newEntry = ZipEntry(entry.name)
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index da53487..83f873d 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -28,10 +28,10 @@
         var inJar: String = "",
 
         /** Output stub jar file */
-        var outStubJar: String = "",
+        var outStubJar: String? = null,
 
         /** Output implementation jar file */
-        var outImplJar: String = "",
+        var outImplJar: String? = null,
 
         var inputJarDumpFile: String? = null,
 
@@ -71,7 +71,7 @@
         var enablePreTrace: Boolean = false,
         var enablePostTrace: Boolean = false,
 
-        var enableNonStubMethodCallDetection: Boolean = true,
+        var enableNonStubMethodCallDetection: Boolean = false,
 ) {
     companion object {
 
@@ -209,11 +209,14 @@
             if (ret.inJar.isEmpty()) {
                 throw ArgumentsException("Required option missing: --in-jar")
             }
-            if (ret.outStubJar.isEmpty()) {
-                throw ArgumentsException("Required option missing: --out-stub-jar")
+            if (ret.outStubJar == null && ret.outImplJar == null) {
+                log.w("Neither --out-stub-jar nor --out-impl-jar is set." +
+                        " $COMMAND_NAME will not generate jar files.")
             }
-            if (ret.outImplJar.isEmpty()) {
-                throw ArgumentsException("Required option missing: --out-impl-jar")
+
+            if (ret.enableNonStubMethodCallDetection) {
+                log.w("--enable-non-stub-method-check is not fully implemented yet." +
+                    " See the todo in doesMethodNeedNonStubCallCheck().")
             }
 
             return ret
diff --git a/tools/hoststubgen/hoststubgen/test-framework/README.md b/tools/hoststubgen/hoststubgen/test-framework/README.md
index 20e2f87..f616ad6 100644
--- a/tools/hoststubgen/hoststubgen/test-framework/README.md
+++ b/tools/hoststubgen/hoststubgen/test-framework/README.md
@@ -14,12 +14,6 @@
 $ atest --no-bazel-mode HostStubGenTest-framework-test-host-test
 ```
 
-- With `run-ravenwood-test`
-
-```
-$ run-ravenwood-test HostStubGenTest-framework-test-host-test
-```
-
 - Advanced option: `run-test-without-atest.sh` runs the test without using `atest` or `run-ravenwood-test`
 
 ```
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md
index f3c0450..3bfad9b 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md
@@ -13,12 +13,6 @@
 $ atest hoststubgen-test-tiny-test
 ```
 
-- With `run-ravenwood-test` should work too. This is the proper way to run it.
-
-```
-$ run-ravenwood-test hoststubgen-test-tiny-test
-```
-
 - `run-test-manually.sh` also run the test, but it builds the stub/impl jars and the test without
   using the build system. This is useful for debugging the tool.
 
diff --git a/tools/hoststubgen/scripts/Android.bp b/tools/hoststubgen/scripts/Android.bp
index 5da805e..b1ba07e 100644
--- a/tools/hoststubgen/scripts/Android.bp
+++ b/tools/hoststubgen/scripts/Android.bp
@@ -18,9 +18,3 @@
     tools: ["dump-jar"],
     cmd: "$(location dump-jar) -s -o $(out) $(in)",
 }
-
-sh_binary_host {
-    name: "run-ravenwood-test",
-    src: "run-ravenwood-test",
-    visibility: ["//visibility:public"],
-}
diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh
index 2dac089..82faa91 100755
--- a/tools/hoststubgen/scripts/run-all-tests.sh
+++ b/tools/hoststubgen/scripts/run-all-tests.sh
@@ -22,10 +22,10 @@
 READY_TEST_MODULES=(
   HostStubGenTest-framework-all-test-host-test
   hoststubgen-test-tiny-test
+  CtsUtilTestCasesRavenwood
 )
 
 MUST_BUILD_MODULES=(
-    run-ravenwood-test
     "${NOT_READY_TEST_MODULES[*]}"
     HostStubGenTest-framework-test
 )
@@ -51,8 +51,6 @@
 # run ./scripts/build-framework-hostside-jars-without-genrules.sh
 
 # These tests should all pass.
-run-ravenwood-test ${READY_TEST_MODULES[*]}
-
-run atest CtsUtilTestCasesRavenwood
+run atest ${READY_TEST_MODULES[*]}
 
 echo ""${0##*/}" finished, with no unexpected failures. Ready to submit!"
\ No newline at end of file
diff --git a/tools/hoststubgen/scripts/run-ravenwood-test b/tools/hoststubgen/scripts/run-ravenwood-test
deleted file mode 100755
index 9bbb859..0000000
--- a/tools/hoststubgen/scripts/run-ravenwood-test
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/bin/bash
-# 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.
-
-set -e
-
-# Script to run a "Ravenwood" host side test.
-#
-# A proper way to run these tests is to use `atest`, but `atest` has a known issue of loading
-# unrelated jar files as the class path, so for now we use this script to run host side tests.
-
-# Copy (with some changes) some functions from ../common.sh, so this script can be used without it.
-
-m() {
-  if (( $SKIP_BUILD )) ; then
-    echo "Skipping build: $*" 1>&2
-    return 0
-  fi
-  run ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@"
-}
-
-run() {
-  echo "Running: $*" 1>&2
-  "$@"
-}
-
-run_junit_test_jar() {
-  local jar="$1"
-  echo "Starting test: $jar ..."
-  run cd "${jar%/*}"
-
-  run ${JAVA:-java} $JAVA_OPTS \
-      -cp $jar \
-      org.junit.runner.JUnitCore \
-      com.android.hoststubgen.hosthelper.HostTestSuite || return 1
-  return 0
-}
-
-help() {
-  cat <<'EOF'
-
-  run-ravenwood-test -- Run Ravenwood host tests
-
-  Usage:
-    run-ravenwood-test [options] MODULE-NAME ...
-
-  Options:
-    -h: Help
-    -t: Run test only, without building
-    -b: Build only, without running
-
-  Example:
-    run-ravenwood-test HostStubGenTest-framework-test-host-test
-
-EOF
-}
-
-#-------------------------------------------------------------------------
-# Parse options
-#-------------------------------------------------------------------------
-build=0
-test=0
-
-while getopts "htb" opt; do
-  case "$opt" in
-    h) help; exit 0 ;;
-    t)
-      test=1
-      ;;
-    b)
-      build=1
-      ;;
-  esac
-done
-shift $(($OPTIND - 1))
-
-# If neither -t nor -b is provided, then build and run./
-if (( ( $build + $test ) == 0 )) ; then
-  build=1
-  test=1
-fi
-
-
-modules=("${@}")
-
-if (( "${#modules[@]}" == 0 )); then
-  help
-  exit 1
-fi
-
-#-------------------------------------------------------------------------
-# Build
-#-------------------------------------------------------------------------
-if (( $build )) ; then
-  run m "${modules[@]}"
-fi
-
-#-------------------------------------------------------------------------
-# Run
-#-------------------------------------------------------------------------
-
-failures=0
-if (( test )) ; then
-  for module in "${modules[@]}"; do
-    if run_junit_test_jar "$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/${module}/${module}.jar"; then
-      : # passed.
-    else
-      failures=$(( failures + 1 ))
-    fi
-  done
-
-  if (( $failures > 0 )) ; then
-    echo "$failures test jar(s) failed." 1>&2
-    exit 2
-  fi
-fi
-
-exit 0
\ No newline at end of file