Merge changes Ibac62793,I45260817 into main

* changes:
  Convert enableDisplays to a StateFlow in the background
  Introduce @Background CoroutineScope
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 16389b3..c80f2ea 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -26,6 +26,7 @@
     ":android.os.flags-aconfig-java{.generated_srcjars}",
     ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
     ":android.security.flags-aconfig-java{.generated_srcjars}",
+    ":android.service.chooser.flags-aconfig-java{.generated_srcjars}",
     ":android.service.notification.flags-aconfig-java{.generated_srcjars}",
     ":android.view.flags-aconfig-java{.generated_srcjars}",
     ":android.view.accessibility.flags-aconfig-java{.generated_srcjars}",
@@ -683,6 +684,19 @@
     aconfig_declarations: "device_policy_aconfig_flags",
 }
 
+// Chooser / "Sharesheet"
+aconfig_declarations {
+    name: "android.service.chooser.flags-aconfig",
+    package: "android.service.chooser",
+    srcs: ["core/java/android/service/chooser/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.service.chooser.flags-aconfig-java",
+    aconfig_declarations: "android.service.chooser.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // JobScheduler
 aconfig_declarations {
     name: "framework-jobscheduler-job.flags-aconfig",
@@ -755,6 +769,13 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+java_aconfig_library {
+    name: "android.hardware.usb.flags-aconfig-java-host",
+    aconfig_declarations: "android.hardware.usb.flags-aconfig",
+    host_supported: true,
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // WindowingTools
 aconfig_declarations {
     name: "android.tracing.flags-aconfig",
diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline
index a4174ee..7992702 100644
--- a/api/javadoc-lint-baseline
+++ b/api/javadoc-lint-baseline
@@ -1,17 +1,3 @@
-// b/303477132
-android/app/appsearch/AppSearchSchema.java:402: lint: Unresolved link/see tag "#getIndexableNestedProperties()" in android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder [101]
-android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.AppSearchSession [101]
-android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.AppSearchSession [101]
-android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.AppSearchSession [101]
-android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#isFeatureSupported" in android.app.appsearch.AppSearchSession [101]
-android/app/appsearch/JoinSpec.java:219: lint: Unresolved link/see tag "android.app.appsearch.SearchSpec.RankingStrategy#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE SearchSpec.RankingStrategy#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE" in android.app.appsearch.JoinSpec.Builder [101]
-android/app/appsearch/SearchSpec.java:230: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.SearchSpec [101]
-android/app/appsearch/SearchSpec.java:237: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.SearchSpec [101]
-android/app/appsearch/SearchSpec.java:244: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.SearchSpec [101]
-android/app/appsearch/SearchSpec.java:913: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
-android/app/appsearch/SearchSpec.java:925: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
-android/app/appsearch/SearchSpec.java:929: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.SearchSpec.Builder [101]
-
 // b/303582215
 android/hardware/camera2/CameraCharacteristics.java:2169: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
 android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
index ec2b1f4..a78a465 100644
--- a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
@@ -96,9 +96,9 @@
     return env;
 }
 
-std::unique_ptr<UinputDevice> UinputDevice::open(int32_t id, const char* name, int32_t vid,
-                                                 int32_t pid, uint16_t bus, uint32_t ffEffectsMax,
-                                                 const char* port,
+std::unique_ptr<UinputDevice> UinputDevice::open(int32_t id, const char* name, int32_t vendorId,
+                                                 int32_t productId, int32_t versionId, uint16_t bus,
+                                                 uint32_t ffEffectsMax, const char* port,
                                                  std::unique_ptr<DeviceCallback> callback) {
     android::base::unique_fd fd(::open(UINPUT_PATH, O_RDWR | O_NONBLOCK | O_CLOEXEC));
     if (!fd.ok()) {
@@ -118,8 +118,9 @@
     strlcpy(setupDescriptor.name, name, UINPUT_MAX_NAME_SIZE);
     setupDescriptor.id.version = 1;
     setupDescriptor.id.bustype = bus;
-    setupDescriptor.id.vendor = vid;
-    setupDescriptor.id.product = pid;
+    setupDescriptor.id.vendor = vendorId;
+    setupDescriptor.id.product = productId;
+    setupDescriptor.id.version = versionId;
     setupDescriptor.ff_effects_max = ffEffectsMax;
 
     // Request device configuration.
@@ -242,9 +243,9 @@
     return data;
 }
 
-static jlong openUinputDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
-                              jint pid, jint bus, jint ffEffectsMax, jstring rawPort,
-                              jobject callback) {
+static jlong openUinputDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id,
+                              jint vendorId, jint productId, jint versionId, jint bus,
+                              jint ffEffectsMax, jstring rawPort, jobject callback) {
     ScopedUtfChars name(env, rawName);
     if (name.c_str() == nullptr) {
         return 0;
@@ -255,8 +256,8 @@
             std::make_unique<uinput::DeviceCallback>(env, callback);
 
     std::unique_ptr<uinput::UinputDevice> d =
-            uinput::UinputDevice::open(id, name.c_str(), vid, pid, bus, ffEffectsMax, port.c_str(),
-                                       std::move(cb));
+            uinput::UinputDevice::open(id, name.c_str(), vendorId, productId, versionId, bus,
+                                       ffEffectsMax, port.c_str(), std::move(cb));
     return reinterpret_cast<jlong>(d.release());
 }
 
@@ -326,7 +327,7 @@
 
 static JNINativeMethod sMethods[] = {
         {"nativeOpenUinputDevice",
-         "(Ljava/lang/String;IIIIILjava/lang/String;"
+         "(Ljava/lang/String;IIIIIILjava/lang/String;"
          "Lcom/android/commands/uinput/Device$DeviceCallback;)J",
          reinterpret_cast<void*>(openUinputDevice)},
         {"nativeInjectEvent", "(JIII)V", reinterpret_cast<void*>(injectEvent)},
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.h b/cmds/uinput/jni/com_android_commands_uinput_Device.h
index 6da3d79..9769a75 100644
--- a/cmds/uinput/jni/com_android_commands_uinput_Device.h
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.h
@@ -46,9 +46,9 @@
 
 class UinputDevice {
 public:
-    static std::unique_ptr<UinputDevice> open(int32_t id, const char* name, int32_t vid,
-                                              int32_t pid, uint16_t bus, uint32_t ff_effects_max,
-                                              const char* port,
+    static std::unique_ptr<UinputDevice> open(int32_t id, const char* name, int32_t vendorId,
+                                              int32_t productId, int32_t versionId, uint16_t bus,
+                                              uint32_t ff_effects_max, const char* port,
                                               std::unique_ptr<DeviceCallback> callback);
 
     virtual ~UinputDevice();
diff --git a/cmds/uinput/src/com/android/commands/uinput/Device.java b/cmds/uinput/src/com/android/commands/uinput/Device.java
index b0fa34c..787055c 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Device.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Device.java
@@ -61,8 +61,9 @@
         System.loadLibrary("uinputcommand_jni");
     }
 
-    private static native long nativeOpenUinputDevice(String name, int id, int vid, int pid,
-            int bus, int ffEffectsMax, String port, DeviceCallback callback);
+    private static native long nativeOpenUinputDevice(String name, int id, int vendorId,
+            int productId, int versionId, int bus, int ffEffectsMax, String port,
+            DeviceCallback callback);
     private static native void nativeCloseUinputDevice(long ptr);
     private static native void nativeInjectEvent(long ptr, int type, int code, int value);
     private static native void nativeConfigure(int handle, int code, int[] configs);
@@ -71,7 +72,7 @@
     private static native int nativeGetEvdevEventCodeByLabel(int type, String label);
     private static native int nativeGetEvdevInputPropByLabel(String label);
 
-    public Device(int id, String name, int vid, int pid, int bus,
+    public Device(int id, String name, int vendorId, int productId, int versionId, int bus,
             SparseArray<int[]> configuration, int ffEffectsMax,
             SparseArray<InputAbsInfo> absInfo, String port) {
         mId = id;
@@ -83,19 +84,20 @@
         mOutputStream = System.out;
         SomeArgs args = SomeArgs.obtain();
         args.argi1 = id;
-        args.argi2 = vid;
-        args.argi3 = pid;
-        args.argi4 = bus;
-        args.argi5 = ffEffectsMax;
+        args.argi2 = vendorId;
+        args.argi3 = productId;
+        args.argi4 = versionId;
+        args.argi5 = bus;
+        args.argi6 = ffEffectsMax;
         if (name != null) {
             args.arg1 = name;
         } else {
-            args.arg1 = id + ":" + vid + ":" + pid;
+            args.arg1 = id + ":" + vendorId + ":" + productId;
         }
         if (port != null) {
             args.arg2 = port;
         } else {
-            args.arg2 = "uinput:" + id + ":" + vid + ":" + pid;
+            args.arg2 = "uinput:" + id + ":" + vendorId + ":" + productId;
         }
 
         mHandler.obtainMessage(MSG_OPEN_UINPUT_DEVICE, args).sendToTarget();
@@ -161,8 +163,10 @@
                 case MSG_OPEN_UINPUT_DEVICE:
                     SomeArgs args = (SomeArgs) msg.obj;
                     String name = (String) args.arg1;
-                    mPtr = nativeOpenUinputDevice(name, args.argi1, args.argi2,
-                            args.argi3, args.argi4, args.argi5, (String) args.arg2,
+                    mPtr = nativeOpenUinputDevice(name, args.argi1 /* id */,
+                            args.argi2 /* vendorId */, args.argi3 /* productId */,
+                            args.argi4 /* versionId */, args.argi5 /* bus */,
+                            args.argi6 /* ffEffectsMax */, (String) args.arg2 /* port */,
                             new DeviceCallback());
                     if (mPtr == 0) {
                         RuntimeException ex = new RuntimeException(
diff --git a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
index b89e2cd..7652f24 100644
--- a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
+++ b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
@@ -19,8 +19,8 @@
 import android.annotation.Nullable;
 import android.util.SparseArray;
 
-import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.LineNumberReader;
 import java.io.Reader;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -47,10 +47,11 @@
     private static final int REGISTRATION_DELAY_MILLIS = 500;
 
     private static class CommentAwareReader {
-        private final BufferedReader mReader;
+        private final LineNumberReader mReader;
+        private String mPreviousLine;
         private String mNextLine;
 
-        CommentAwareReader(BufferedReader in) throws IOException {
+        CommentAwareReader(LineNumberReader in) throws IOException {
             mReader = in;
             mNextLine = findNextLine();
         }
@@ -90,12 +91,46 @@
 
         /** Moves to the next line of the file. */
         public void advance() throws IOException {
+            mPreviousLine = mNextLine;
             mNextLine = findNextLine();
         }
 
         public boolean isAtEndOfFile() {
             return mNextLine == null;
         }
+
+        /** Returns the previous line, for error messages. */
+        public String getPreviousLine() {
+            return mPreviousLine;
+        }
+
+        /** Returns the number of the <b>previous</b> line. */
+        public int getPreviousLineNumber() {
+            return mReader.getLineNumber() - 1;
+        }
+    }
+
+    public static class ParsingException extends RuntimeException {
+        private final int mLineNumber;
+        private final String mLine;
+
+        ParsingException(String message, CommentAwareReader reader) {
+            this(message, reader.getPreviousLine(), reader.getPreviousLineNumber());
+        }
+
+        ParsingException(String message, String line, int lineNumber) {
+            super(message);
+            mLineNumber = lineNumber;
+            mLine = line;
+        }
+
+        /** Returns a nicely formatted error message, including the line number and line. */
+        public String makeErrorMessage() {
+            return String.format("""
+                    Parsing error on line %d: %s
+                    --> %s
+                    """, mLineNumber, getMessage(), mLine);
+        }
     }
 
     private final CommentAwareReader mReader;
@@ -107,7 +142,7 @@
     private final Queue<Event> mQueuedEvents = new ArrayDeque<>(2);
 
     public EvemuParser(Reader in) throws IOException {
-        mReader = new CommentAwareReader(new BufferedReader(in));
+        mReader = new CommentAwareReader(new LineNumberReader(in));
         mQueuedEvents.add(parseRegistrationEvent());
 
         // The kernel takes a little time to set up an evdev device after the initial
@@ -133,20 +168,22 @@
             return null;
         }
 
-        final String[] parts = expectLineWithParts("E", 4);
+        final String line = expectLine("E");
+        final String[] parts = expectParts(line, 4);
         final String[] timeParts = parts[0].split("\\.");
         if (timeParts.length != 2) {
-            throw new RuntimeException("Invalid timestamp (does not contain a '.')");
+            throw new ParsingException(
+                    "Invalid timestamp '" + parts[0] + "' (should contain a single '.')", mReader);
         }
         // 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]);
+                parseLong(timeParts[0], 10) * 1_000_000 + parseInt(timeParts[1], 10);
         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]);
+        final int eventType = parseInt(parts[1], 16);
+        final int eventCode = parseInt(parts[2], 16);
+        final int value = parseInt(parts[3], 10);
         eb.setInjections(new int[] {eventType, eventCode, value});
 
         if (mLastEventTimeMicros == -1) {
@@ -184,11 +221,12 @@
         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 String idsLine = expectLine("I");
+        final String[] idStrings = expectParts(idsLine, 4);
+        eb.setBusId(parseInt(idStrings[0], 16));
+        eb.setVendorId(parseInt(idStrings[1], 16));
+        eb.setProductId(parseInt(idStrings[2], 16));
+        eb.setVersionId(parseInt(idStrings[3], 16));
 
         final SparseArray<int[]> config = new SparseArray<>();
         config.append(Event.UinputControlCode.UI_SET_PROPBIT.getValue(), parseProperties());
@@ -215,33 +253,39 @@
     }
 
     private int[] parseProperties() throws IOException {
-        final List<String> propBitmapParts = new ArrayList<>();
+        final ArrayList<Integer> propBitmapParts = new ArrayList<>();
         String line = acceptLine("P");
         while (line != null) {
-            propBitmapParts.addAll(List.of(line.strip().split(" ")));
+            String[] parts = line.strip().split(" ");
+            propBitmapParts.ensureCapacity(propBitmapParts.size() + parts.length);
+            for (String part : parts) {
+                propBitmapParts.add(parseBitmapPart(part, line));
+            }
             line = acceptLine("P");
         }
-        return hexStringBitmapToEventCodes(propBitmapParts);
+        return bitmapToEventCodes(propBitmapParts);
     }
 
     private void parseAxisBitmaps(SparseArray<int[]> config) throws IOException {
-        final Map<Integer, List<String>> axisBitmapParts = new HashMap<>();
+        final Map<Integer, ArrayList<Integer>> axisBitmapParts = new HashMap<>();
         String line = acceptLine("B");
         while (line != null) {
             final String[] parts = line.strip().split(" ");
             if (parts.length < 2) {
-                throw new RuntimeException(
+                throw new ParsingException(
                         "Expected event type and at least one bitmap byte on 'B:' line; only found "
-                                + parts.length + " elements");
+                                + parts.length + " elements", mReader);
             }
-            final int eventType = Integer.parseInt(parts[0], 16);
+            final int eventType = 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<>());
                 }
+                ArrayList<Integer> bitmapParts = axisBitmapParts.get(eventType);
+                bitmapParts.ensureCapacity(bitmapParts.size() + parts.length);
                 for (int i = 1; i < parts.length; i++) {
-                    axisBitmapParts.get(eventType).add(parts[i]);
+                    axisBitmapParts.get(eventType).add(parseBitmapPart(parts[i], line));
                 }
             }
             line = acceptLine("B");
@@ -253,7 +297,7 @@
             }
             final Event.UinputControlCode controlCode =
                     Event.UinputControlCode.forEventType(entry.getKey());
-            final int[] eventCodes = hexStringBitmapToEventCodes(entry.getValue());
+            final int[] eventCodes = bitmapToEventCodes(entry.getValue());
             if (controlCode != null && eventCodes.length > 0) {
                 config.append(controlCode.getValue(), eventCodes);
                 eventTypesToSet.add(entry.getKey());
@@ -263,24 +307,33 @@
                 Event.UinputControlCode.UI_SET_EVBIT.getValue(), unboxIntList(eventTypesToSet));
     }
 
+    private int parseBitmapPart(String part, String line) {
+        int b = parseInt(part, 16);
+        if (b < 0x0 || b > 0xff) {
+            throw new ParsingException("Bitmap part '" + part
+                    + "' invalid; parts must be hexadecimal values between 00 and ff.", mReader);
+        }
+        return b;
+    }
+
     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> "
+                throw new ParsingException(
+                        "AbsInfo lines should have the format 'A: <index (hex)> <min> <max> <fuzz> "
                                 + "<flat> [<resolution>]'; expected 5 or 6 numbers but found "
-                                + parts.length);
+                                + parts.length, mReader);
             }
-            final int axisCode = Integer.parseInt(parts[0], 16);
+            final int axisCode = 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;
+            info.minimum = parseInt(parts[1], 10);
+            info.maximum = parseInt(parts[2], 10);
+            info.fuzz = parseInt(parts[3], 10);
+            info.flat = parseInt(parts[4], 10);
+            info.resolution = parts.length > 5 ? parseInt(parts[5], 10) : 0;
             absInfos.append(axisCode, info);
             line = acceptLine("A");
         }
@@ -305,7 +358,9 @@
     private String expectLine(String type) throws IOException {
         final String line = acceptLine(type);
         if (line == null) {
-            throw new RuntimeException("Expected line of type '" + type + "'");
+            throw new ParsingException("Expected line of type '" + type + "'. (Lines should be in "
+                    + "the order N, I, P, B, A, L, S, E.)",
+                    mReader.peekLine(), mReader.getPreviousLineNumber() + 1);
         } else {
             return line;
         }
@@ -325,9 +380,8 @@
         }
         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 ': '");
+            throw new ParsingException("Missing type separator ': '",
+                    line, mReader.getPreviousLineNumber() + 1);
         }
         if (lineParts[0].equals(type)) {
             mReader.advance();
@@ -337,31 +391,37 @@
         }
     }
 
-    /**
-     * 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(" ");
+    private String[] expectParts(String line, int numParts) {
+        final String[] parts = line.strip().split(" ");
         if (parts.length != numParts) {
-            throw new RuntimeException("Expected a '" + type + "' line with " + numParts
-                    + " parts, found one with " + parts.length);
+            throw new ParsingException(
+                    "Expected a line with " + numParts + " space-separated parts, but found one "
+                            + "with " + parts.length, mReader);
         }
         return parts;
     }
 
-    private static int[] hexStringBitmapToEventCodes(List<String> strs) {
+    private int parseInt(String s, int radix) {
+        try {
+            return Integer.parseInt(s, radix);
+        } catch (NumberFormatException ex) {
+            throw new ParsingException(
+                    "'" + s + "' is not a valid integer of base " + radix, mReader);
+        }
+    }
+
+    private long parseLong(String s, int radix) {
+        try {
+            return Long.parseLong(s, radix);
+        } catch (NumberFormatException ex) {
+            throw new ParsingException("'" + s + "' is not a valid long of base " + radix, mReader);
+        }
+    }
+
+    private static int[] bitmapToEventCodes(List<Integer> bytes) {
         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 iByte = 0; iByte < bytes.size(); iByte++) {
+            int b = bytes.get(iByte);
             for (int iBit = 0; iBit < 8; iBit++) {
                 if ((b & 1) != 0) {
                     codes.add(iByte * 8 + iBit);
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 5ec40e5..0f16a27 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -94,8 +94,9 @@
     private int mId;
     private Command mCommand;
     private String mName;
-    private int mVid;
-    private int mPid;
+    private int mVendorId;
+    private int mProductId;
+    private int mVersionId;
     private int mBusId;
     private int[] mInjections;
     private SparseArray<int[]> mConfiguration;
@@ -118,11 +119,15 @@
     }
 
     public int getVendorId() {
-        return mVid;
+        return mVendorId;
     }
 
     public int getProductId() {
-        return mPid;
+        return mProductId;
+    }
+
+    public int getVersionId() {
+        return mVersionId;
     }
 
     public int getBus() {
@@ -172,8 +177,8 @@
         return "Event{id=" + mId
             + ", command=" + mCommand
             + ", name=" + mName
-            + ", vid=" + mVid
-            + ", pid=" + mPid
+            + ", vid=" + mVendorId
+            + ", pid=" + mProductId
             + ", busId=" + mBusId
             + ", events=" + Arrays.toString(mInjections)
             + ", configuration=" + mConfiguration
@@ -216,12 +221,16 @@
             mEvent.mConfiguration = configuration;
         }
 
-        public void setVid(int vid) {
-            mEvent.mVid = vid;
+        public void setVendorId(int vendorId) {
+            mEvent.mVendorId = vendorId;
         }
 
-        public void setPid(int pid) {
-            mEvent.mPid = pid;
+        public void setProductId(int productId) {
+            mEvent.mProductId = productId;
+        }
+
+        public void setVersionId(int versionId) {
+            mEvent.mVersionId = versionId;
         }
 
         public void setBusId(int busId) {
diff --git a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
index 888ec5a..ed3ff33 100644
--- a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
+++ b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
@@ -60,8 +60,8 @@
                         case "command" -> eb.setCommand(
                                 Event.Command.valueOf(mReader.nextString().toUpperCase()));
                         case "name" -> eb.setName(mReader.nextString());
-                        case "vid" -> eb.setVid(readInt());
-                        case "pid" -> eb.setPid(readInt());
+                        case "vid" -> eb.setVendorId(readInt());
+                        case "pid" -> eb.setProductId(readInt());
                         case "bus" -> eb.setBusId(readBus());
                         case "events" -> {
                             int[] injections = readInjectedEvents().stream()
diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
index 684a12f..04df279 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
@@ -60,6 +60,10 @@
                 stream = new FileInputStream(f);
             }
             (new Uinput(stream)).run();
+        } catch (EvemuParser.ParsingException e) {
+            System.err.println(e.makeErrorMessage());
+            error(e.makeErrorMessage(), e);
+            System.exit(1);
         } catch (Exception e) {
             error("Uinput injection failed.", e);
             System.exit(1);
@@ -142,8 +146,9 @@
                     "Tried to send command \"" + e.getCommand() + "\" to an unregistered device!");
         }
         int id = e.getId();
-        Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(),
-                e.getConfiguration(), e.getFfEffectsMax(), e.getAbsInfo(), e.getPort());
+        Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(),
+                e.getVersionId(), e.getBus(), e.getConfiguration(), e.getFfEffectsMax(),
+                e.getAbsInfo(), e.getPort());
         mDevices.append(id, d);
     }
 
diff --git a/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java b/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java
index 6ee987f..06b0aac2 100644
--- a/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java
+++ b/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java
@@ -19,6 +19,8 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assert.fail;
+
 import android.platform.test.annotations.Postsubmit;
 import android.util.SparseArray;
 
@@ -68,7 +70,7 @@
         assertThat(event.getBus()).isEqualTo(0x0001);
         assertThat(event.getVendorId()).isEqualTo(0x1234);
         assertThat(event.getProductId()).isEqualTo(0x5678);
-        // TODO(b/302297266): check version ID once it's supported
+        assertThat(event.getVersionId()).isEqualTo(0x9abc);
     }
 
     @Test
@@ -241,6 +243,25 @@
     }
 
     @Test
+    public void testErrorLineNumberReporting() throws IOException {
+        StringReader reader = new StringReader("""
+                # EVEMU 1.3
+                N: ACME Widget
+                # Comment to make sure they're taken into account when numbering lines
+                I: 0001 1234 5678 9abc
+                00 00 00 00 00 00 00 00  # Missing a type
+                E: 0.000001 0001 0015 0001   # KEY_Y press
+                E: 0.000001 0000 0000 0000   # SYN_REPORT
+                """);
+        try {
+            new EvemuParser(reader);
+            fail("Parser should have thrown an error about the line with the missing type.");
+        } catch (EvemuParser.ParsingException ex) {
+            assertThat(ex.makeErrorMessage()).startsWith("Parsing error on line 5:");
+        }
+    }
+
+    @Test
     public void testFreeDesktopEvemuRecording() throws IOException {
         // This is a real recording from FreeDesktop's evemu-record tool, as a basic compatibility
         // check with the FreeDesktop tools.
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 01ca6d9..ce5752f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4211,10 +4211,18 @@
 
   public final class UserProperties implements android.os.Parcelable {
     method public int describeContents();
+    method public int getShowInQuietMode();
+    method public int getShowInSharingSurfaces();
     method public boolean isCredentialShareableWithParent();
     method public boolean isMediaSharedWithParent();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR;
+    field public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2; // 0x2
+    field public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1; // 0x1
+    field public static final int SHOW_IN_QUIET_MODE_PAUSED = 0; // 0x0
+    field public static final int SHOW_IN_SHARING_SURFACES_NO = 2; // 0x2
+    field public static final int SHOW_IN_SHARING_SURFACES_SEPARATE = 1; // 0x1
+    field public static final int SHOW_IN_SHARING_SURFACES_WITH_PARENT = 0; // 0x0
   }
 
 }
@@ -10560,6 +10568,7 @@
     method @NonNull public java.util.List<android.os.UserHandle> getEnabledProfiles();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getMainUser();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getPreviousForegroundUser();
+    method @NonNull public String getProfileLabel();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableProfileCount(@NonNull String);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableUserCount(@NonNull String);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f3bad3a..1f7390c 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -4201,8 +4201,13 @@
   public static class WindowInfosListenerForTest.WindowInfo {
     field @NonNull public final android.graphics.Rect bounds;
     field public final int displayId;
+    field public final boolean isDuplicateTouchToWallpaper;
+    field public final boolean isFocusable;
+    field public final boolean isPreventSplitting;
+    field public final boolean isTouchable;
     field public final boolean isTrustedOverlay;
     field public final boolean isVisible;
+    field public final boolean isWatchOutsideTouch;
     field @NonNull public final String name;
     field @NonNull public final android.graphics.Matrix transform;
     field @NonNull public final android.os.IBinder windowToken;
diff --git a/core/java/Android.bp b/core/java/Android.bp
index dfe3344..fb1e16a 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -546,6 +546,15 @@
     ],
 }
 
+// PackageManager common
+filegroup {
+    name: "framework-pm-common-shared-srcs",
+    srcs: [
+        "com/android/server/pm/pkg/AndroidPackage.java",
+        "com/android/server/pm/pkg/AndroidPackageSplit.java",
+    ],
+}
+
 java_library {
     name: "protolog-lib",
     platform_apis: true,
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index a4c3bb8..87c86df 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2934,6 +2934,15 @@
     }
 
     @Override
+    public String getSuspendingPackage(String suspendedPackage) {
+        try {
+            return mPM.getSuspendingPackage(suspendedPackage, getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public boolean isPackageSuspendedForUser(String packageName, int userId) {
         try {
             return mPM.isPackageSuspendedForUser(packageName, userId);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index df6badc..d540748 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -328,7 +328,7 @@
      * A splash screen view has copied.
      */
     void onSplashScreenViewCopyFinished(int taskId,
-            in SplashScreenView.SplashScreenViewParcelable material);
+            in @nullable SplashScreenView.SplashScreenViewParcelable material);
 
     /**
      * When the Picture-in-picture state has changed.
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 7be4b36..fb0edb9 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -24,8 +24,15 @@
 
 flag {
   name: "lifetime_extension_refactor"
-    namespace: "systemui"
-    description: "Enables moving notification lifetime extension management from SystemUI to "
-        "Notification Manager Service"
-    bug: "299448097"
-}
\ No newline at end of file
+  namespace: "systemui"
+  description: "Enables moving notification lifetime extension management from SystemUI to "
+      "Notification Manager Service"
+  bug: "299448097"
+}
+
+flag {
+  name: "visit_risky_uris"
+  namespace: "systemui"
+  description: "Guards the security fix that ensures all URIs in intents and Person.java are valid"
+  bug: "281044385"
+}
diff --git a/core/java/android/app/time/TEST_MAPPING b/core/java/android/app/time/TEST_MAPPING
index 47a152a..0f7a070 100644
--- a/core/java/android/app/time/TEST_MAPPING
+++ b/core/java/android/app/time/TEST_MAPPING
@@ -7,20 +7,6 @@
           "include-filter": "android.app."
         }
       ]
-    }
-  ],
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
-    {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.timezonedetector."
-        },
-        {
-          "include-filter": "com.android.server.timedetector."
-        }
-      ]
     },
     {
       "name": "CtsTimeTestCases",
@@ -30,5 +16,19 @@
         }
       ]
     }
+  ],
+  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+  "postsubmit": [
+    {
+      "name": "FrameworksTimeServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.timezonedetector."
+        },
+        {
+          "include-filter": "com.android.server.timedetector."
+        }
+      ]
+    }
   ]
 }
diff --git a/core/java/android/app/timedetector/TEST_MAPPING b/core/java/android/app/timedetector/TEST_MAPPING
index 9517fb9..43dd82f 100644
--- a/core/java/android/app/timedetector/TEST_MAPPING
+++ b/core/java/android/app/timedetector/TEST_MAPPING
@@ -7,17 +7,6 @@
           "include-filter": "android.app."
         }
       ]
-    }
-  ],
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
-    {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.timedetector."
-        }
-      ]
     },
     {
       "name": "CtsTimeTestCases",
@@ -27,5 +16,16 @@
         }
       ]
     }
+  ],
+  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+  "postsubmit": [
+    {
+      "name": "FrameworksTimeServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.timedetector."
+        }
+      ]
+    }
   ]
 }
diff --git a/core/java/android/app/timezonedetector/TEST_MAPPING b/core/java/android/app/timezonedetector/TEST_MAPPING
index fd41b86..2be5614 100644
--- a/core/java/android/app/timezonedetector/TEST_MAPPING
+++ b/core/java/android/app/timezonedetector/TEST_MAPPING
@@ -7,17 +7,6 @@
           "include-filter": "android.app."
         }
       ]
-    }
-  ],
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
-    {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.timezonedetector."
-        }
-      ]
     },
     {
       "name": "CtsTimeTestCases",
@@ -27,5 +16,16 @@
         }
       ]
     }
+  ],
+  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+  "postsubmit": [
+    {
+      "name": "FrameworksTimeServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.timezonedetector."
+        }
+      ]
+    }
   ]
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index babfba1..98623de 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -312,6 +312,8 @@
 
     Bundle getSuspendedPackageAppExtras(String packageName, int userId);
 
+    String getSuspendingPackage(String packageName, int userId);
+
     /**
      * Backup/restore support - only the system uid may use these.
      */
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fe31c9d..6775f9b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -9965,6 +9965,21 @@
     }
 
     /**
+     * Get the name of the package that suspended the given package. Packages can be suspended by
+     * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#SUSPEND_APPS}.
+     *
+     * @param suspendedPackage The package that has been suspended.
+     * @return Name of the package that suspended the given package. Returns {@code null} if the
+     * given package is not currently suspended and the platform package name - i.e.
+     * {@code "android"} - if the package was suspended by a device admin.
+     * @hide
+     */
+    public @Nullable String getSuspendingPackage(@NonNull String suspendedPackage) {
+        throw new UnsupportedOperationException("getSuspendingPackage not implemented");
+    }
+
+    /**
      * Query if an app is currently stopped.
      *
      * @return {@code true} if the given package is stopped, {@code false} otherwise
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index f532c4c..445ca0c 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.os.Parcel;
@@ -49,7 +50,8 @@
     private static final String ATTR_SHOW_IN_LAUNCHER = "showInLauncher";
     private static final String ATTR_START_WITH_PARENT = "startWithParent";
     private static final String ATTR_SHOW_IN_SETTINGS = "showInSettings";
-    private static final String ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE = "hideInSettingsInQuietMode";
+    private static final String ATTR_SHOW_IN_QUIET_MODE = "showInQuietMode";
+    private static final String ATTR_SHOW_IN_SHARING_SURFACES = "showInSharingSurfaces";
     private static final String ATTR_INHERIT_DEVICE_POLICY = "inheritDevicePolicy";
     private static final String ATTR_USE_PARENTS_CONTACTS = "useParentsContacts";
     private static final String ATTR_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA =
@@ -81,7 +83,8 @@
             INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT,
             INDEX_DELETE_APP_WITH_PARENT,
             INDEX_ALWAYS_VISIBLE,
-            INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE,
+            INDEX_SHOW_IN_QUIET_MODE,
+            INDEX_SHOW_IN_SHARING_SURFACES,
             INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -99,8 +102,9 @@
     private static final int INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT = 9;
     private static final int INDEX_DELETE_APP_WITH_PARENT = 10;
     private static final int INDEX_ALWAYS_VISIBLE = 11;
-    private static final int INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE = 12;
+    private static final int INDEX_SHOW_IN_QUIET_MODE = 12;
     private static final int INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE = 13;
+    private static final int INDEX_SHOW_IN_SHARING_SURFACES = 14;
     /** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
     private long mPropertiesPresent = 0;
 
@@ -286,6 +290,81 @@
      */
     public static final int CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING = 1;
 
+    /**
+     * Possible values for the profile visibility when in quiet mode. This affects the profile data
+     * and apps surfacing in Settings, sharing surfaces, and file picker surfaces. It signifies
+     * whether the profile data and apps will be shown or not.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SHOW_IN_QUIET_MODE_",
+            value = {
+                    SHOW_IN_QUIET_MODE_PAUSED,
+                    SHOW_IN_QUIET_MODE_HIDDEN,
+                    SHOW_IN_QUIET_MODE_DEFAULT,
+            }
+    )
+    public @interface ShowInQuietMode {
+    }
+
+    /**
+     * Indicates that the profile should still be visible in quiet mode but should be shown as
+     * paused (e.g. by greying out its icons).
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int SHOW_IN_QUIET_MODE_PAUSED = 0;
+    /**
+     * Indicates that the profile should not be visible when the profile is in quiet mode.
+     * For example, the profile should not be shown in tabbed views in Settings, files sharing
+     * surfaces etc when in quiet mode.
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1;
+    /**
+     * Indicates that quiet mode should not have any effect on the profile visibility. If the
+     * profile is meant to be visible, it will remain visible and vice versa.
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2;
+
+    /**
+     * Possible values for the profile apps visibility in sharing surfaces. This indicates the
+     * profile data and apps should be shown in separate tabs or mixed with its parent user's data
+     * and apps in sharing surfaces and file picker surfaces.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SHOW_IN_SHARING_SURFACES_",
+            value = {
+                    SHOW_IN_SHARING_SURFACES_SEPARATE,
+                    SHOW_IN_SHARING_SURFACES_WITH_PARENT,
+                    SHOW_IN_SHARING_SURFACES_NO,
+            }
+    )
+    public @interface ShowInSharingSurfaces {
+    }
+
+    /**
+     * Indicates that the profile data and apps should be shown in sharing surfaces intermixed with
+     * parent user's data and apps.
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int SHOW_IN_SHARING_SURFACES_WITH_PARENT = SHOW_IN_LAUNCHER_WITH_PARENT;
+
+    /**
+     * Indicates that the profile data and apps should be shown in sharing surfaces separate from
+     * parent user's data and apps.
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int SHOW_IN_SHARING_SURFACES_SEPARATE = SHOW_IN_LAUNCHER_SEPARATE;
+
+    /**
+     * Indicates that the profile data and apps should not be shown in sharing surfaces at all.
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int SHOW_IN_SHARING_SURFACES_NO = SHOW_IN_LAUNCHER_NO;
 
     /**
      * Creates a UserProperties (intended for the SystemServer) that stores a reference to the given
@@ -331,7 +410,6 @@
         if (hasManagePermission) {
             // Add items that require MANAGE_USERS or stronger.
             setShowInSettings(orig.getShowInSettings());
-            setHideInSettingsInQuietMode(orig.getHideInSettingsInQuietMode());
             setUseParentsContacts(orig.getUseParentsContacts());
             setAuthAlwaysRequiredToDisableQuietMode(
                     orig.isAuthAlwaysRequiredToDisableQuietMode());
@@ -343,6 +421,8 @@
         setShowInLauncher(orig.getShowInLauncher());
         setMediaSharedWithParent(orig.isMediaSharedWithParent());
         setCredentialShareableWithParent(orig.isCredentialShareableWithParent());
+        setShowInQuietMode(orig.getShowInQuietMode());
+        setShowInSharingSurfaces(orig.getShowInSharingSurfaces());
     }
 
     /**
@@ -419,40 +499,59 @@
     private @ShowInSettings int mShowInSettings;
 
     /**
-     * Returns whether a user should be shown in the Settings app depending on the quiet mode.
-     * This is generally inapplicable for non-profile users.
+     * Returns whether a user should be shown in the Settings and sharing surfaces depending on the
+     * {@link android.os.UserManager#requestQuietModeEnabled(boolean, android.os.UserHandle)
+     * quiet mode}. This is only applicable to profile users since the quiet mode concept is only
+     * applicable to profile users.
      *
-     * <p> {@link #getShowInSettings()} returns whether / how a user should be shown in Settings.
-     * However, if this behaviour should be changed based on the quiet mode of the user, then this
-     * property can be used. If the property is not set then the user is shown in the Settings app
-     * irrespective of whether the user is in quiet mode or not. If the property is set, then the
-     * user is shown in the Settings app only if the user is not in the quiet mode. Please note that
-     * this property takes effect only if {@link #getShowInSettings()} does not return
-     * {@link #SHOW_IN_SETTINGS_NO}.
+     * <p> Please note that, in Settings, this property takes effect only if
+     * {@link #getShowInSettings()} does not return {@link #SHOW_IN_SETTINGS_NO}.
+     * Also note that in Sharing surfaces this property takes effect only if
+     * {@link #getShowInSharingSurfaces()} does not return {@link #SHOW_IN_SHARING_SURFACES_NO}.
      *
-     * <p> The caller must have {@link android.Manifest.permission#MANAGE_USERS} to query this
-     * property.
-     *
-     * @return true if a profile should be shown in the Settings only when the user is not in the
-     * quiet mode.
-     *
-     * See also {@link #getShowInSettings()}, {@link #setShowInSettings(int)},
-     * {@link ShowInSettings}
-     *
-     * @hide
+     * @return One of {@link #SHOW_IN_QUIET_MODE_HIDDEN},
+     *         {@link #SHOW_IN_QUIET_MODE_PAUSED}, or
+     *         {@link #SHOW_IN_QUIET_MODE_DEFAULT} depending on whether the profile should be
+     *         shown in quiet mode or not.
      */
-    public boolean getHideInSettingsInQuietMode() {
-        if (isPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE)) return mHideInSettingsInQuietMode;
-        if (mDefaultProperties != null) return mDefaultProperties.mHideInSettingsInQuietMode;
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public @ShowInQuietMode int getShowInQuietMode() {
+        // NOTE: Launcher currently does not make use of this property.
+        if (isPresent(INDEX_SHOW_IN_QUIET_MODE)) return mShowInQuietMode;
+        if (mDefaultProperties != null) return mDefaultProperties.mShowInQuietMode;
         throw new SecurityException(
-                "You don't have permission to query HideInSettingsInQuietMode");
+                "You don't have permission to query ShowInQuietMode");
     }
     /** @hide */
-    public void setHideInSettingsInQuietMode(boolean hideInSettingsInQuietMode) {
-        this.mHideInSettingsInQuietMode = hideInSettingsInQuietMode;
-        setPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE);
+    public void setShowInQuietMode(@ShowInQuietMode int showInQuietMode) {
+        this.mShowInQuietMode = showInQuietMode;
+        setPresent(INDEX_SHOW_IN_QUIET_MODE);
     }
-    private boolean mHideInSettingsInQuietMode;
+    private int mShowInQuietMode;
+
+    /**
+     * Returns whether a user's data and apps should be shown in sharing surfaces in a separate tab
+     * or mixed with the parent user's data/apps. This is only applicable to profile users.
+     *
+     * @return One of {@link #SHOW_IN_SHARING_SURFACES_NO},
+     *         {@link #SHOW_IN_SHARING_SURFACES_SEPARATE}, or
+     *         {@link #SHOW_IN_SHARING_SURFACES_WITH_PARENT} depending on whether the profile
+     *         should be shown separate from its parent's data, mixed with the parent's data, or
+     *         not shown at all.
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public @ShowInSharingSurfaces int getShowInSharingSurfaces() {
+        if (isPresent(INDEX_SHOW_IN_SHARING_SURFACES)) return mShowInSharingSurfaces;
+        if (mDefaultProperties != null) return mDefaultProperties.mShowInSharingSurfaces;
+        throw new SecurityException(
+                "You don't have permission to query ShowInSharingSurfaces");
+    }
+    /** @hide */
+    public void setShowInSharingSurfaces(@ShowInSharingSurfaces int showInSharingSurfaces) {
+        this.mShowInSharingSurfaces = showInSharingSurfaces;
+        setPresent(INDEX_SHOW_IN_SHARING_SURFACES);
+    }
+    private int mShowInSharingSurfaces;
 
     /**
      * Returns whether a profile should be started when its parent starts (unless in quiet mode).
@@ -799,8 +898,11 @@
                 case ATTR_SHOW_IN_SETTINGS:
                     setShowInSettings(parser.getAttributeInt(i));
                     break;
-                case ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE:
-                    setHideInSettingsInQuietMode(parser.getAttributeBoolean(i));
+                case ATTR_SHOW_IN_QUIET_MODE:
+                    setShowInQuietMode(parser.getAttributeInt(i));
+                    break;
+                case ATTR_SHOW_IN_SHARING_SURFACES:
+                    setShowInSharingSurfaces(parser.getAttributeInt(i));
                     break;
                 case ATTR_INHERIT_DEVICE_POLICY:
                     setInheritDevicePolicy(parser.getAttributeInt(i));
@@ -858,9 +960,12 @@
         if (isPresent(INDEX_SHOW_IN_SETTINGS)) {
             serializer.attributeInt(null, ATTR_SHOW_IN_SETTINGS, mShowInSettings);
         }
-        if (isPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE)) {
-            serializer.attributeBoolean(null, ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE,
-                    mHideInSettingsInQuietMode);
+        if (isPresent(INDEX_SHOW_IN_QUIET_MODE)) {
+            serializer.attributeInt(null, ATTR_SHOW_IN_QUIET_MODE,
+                    mShowInQuietMode);
+        }
+        if (isPresent(INDEX_SHOW_IN_SHARING_SURFACES)) {
+            serializer.attributeInt(null, ATTR_SHOW_IN_SHARING_SURFACES, mShowInSharingSurfaces);
         }
         if (isPresent(INDEX_INHERIT_DEVICE_POLICY)) {
             serializer.attributeInt(null, ATTR_INHERIT_DEVICE_POLICY,
@@ -912,7 +1017,8 @@
         dest.writeInt(mShowInLauncher);
         dest.writeBoolean(mStartWithParent);
         dest.writeInt(mShowInSettings);
-        dest.writeBoolean(mHideInSettingsInQuietMode);
+        dest.writeInt(mShowInQuietMode);
+        dest.writeInt(mShowInSharingSurfaces);
         dest.writeInt(mInheritDevicePolicy);
         dest.writeBoolean(mUseParentsContacts);
         dest.writeBoolean(mUpdateCrossProfileIntentFiltersOnOTA);
@@ -936,7 +1042,8 @@
         mShowInLauncher = source.readInt();
         mStartWithParent = source.readBoolean();
         mShowInSettings = source.readInt();
-        mHideInSettingsInQuietMode = source.readBoolean();
+        mShowInQuietMode = source.readInt();
+        mShowInSharingSurfaces = source.readInt();
         mInheritDevicePolicy = source.readInt();
         mUseParentsContacts = source.readBoolean();
         mUpdateCrossProfileIntentFiltersOnOTA = source.readBoolean();
@@ -974,7 +1081,10 @@
         private @ShowInLauncher int mShowInLauncher = SHOW_IN_LAUNCHER_WITH_PARENT;
         private boolean mStartWithParent = false;
         private @ShowInSettings int mShowInSettings = SHOW_IN_SETTINGS_WITH_PARENT;
-        private boolean mHideInSettingsInQuietMode = false;
+        private @ShowInQuietMode int mShowInQuietMode =
+                SHOW_IN_QUIET_MODE_PAUSED;
+        private @ShowInSharingSurfaces int mShowInSharingSurfaces =
+                SHOW_IN_SHARING_SURFACES_SEPARATE;
         private @InheritDevicePolicy int mInheritDevicePolicy = INHERIT_DEVICE_POLICY_NO;
         private boolean mUseParentsContacts = false;
         private boolean mUpdateCrossProfileIntentFiltersOnOTA = false;
@@ -1005,9 +1115,15 @@
             return this;
         }
 
-        /** Sets the value for {@link #mHideInSettingsInQuietMode} */
-        public Builder setHideInSettingsInQuietMode(boolean hideInSettingsInQuietMode) {
-            mHideInSettingsInQuietMode = hideInSettingsInQuietMode;
+        /** Sets the value for {@link #mShowInQuietMode} */
+        public Builder setShowInQuietMode(@ShowInQuietMode int showInQuietMode) {
+            mShowInQuietMode = showInQuietMode;
+            return this;
+        }
+
+        /** Sets the value for {@link #mShowInSharingSurfaces}. */
+        public Builder setShowInSharingSurfaces(@ShowInSharingSurfaces int showInSharingSurfaces) {
+            mShowInSharingSurfaces = showInSharingSurfaces;
             return this;
         }
 
@@ -1081,7 +1197,8 @@
                     mShowInLauncher,
                     mStartWithParent,
                     mShowInSettings,
-                    mHideInSettingsInQuietMode,
+                    mShowInQuietMode,
+                    mShowInSharingSurfaces,
                     mInheritDevicePolicy,
                     mUseParentsContacts,
                     mUpdateCrossProfileIntentFiltersOnOTA,
@@ -1100,7 +1217,8 @@
             @ShowInLauncher int showInLauncher,
             boolean startWithParent,
             @ShowInSettings int showInSettings,
-            boolean hideInSettingsInQuietMode,
+            @ShowInQuietMode int showInQuietMode,
+            @ShowInSharingSurfaces int showInSharingSurfaces,
             @InheritDevicePolicy int inheritDevicePolicy,
             boolean useParentsContacts, boolean updateCrossProfileIntentFiltersOnOTA,
             @CrossProfileIntentFilterAccessControlLevel int crossProfileIntentFilterAccessControl,
@@ -1114,7 +1232,8 @@
         setShowInLauncher(showInLauncher);
         setStartWithParent(startWithParent);
         setShowInSettings(showInSettings);
-        setHideInSettingsInQuietMode(hideInSettingsInQuietMode);
+        setShowInQuietMode(showInQuietMode);
+        setShowInSharingSurfaces(showInSharingSurfaces);
         setInheritDevicePolicy(inheritDevicePolicy);
         setUseParentsContacts(useParentsContacts);
         setUpdateCrossProfileIntentFiltersOnOTA(updateCrossProfileIntentFiltersOnOTA);
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 9ec082a..6c6b33b 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -42,3 +42,10 @@
     description: "Allow using all cpu cores during a user switch."
     bug: "308105403"
 }
+
+flag {
+    name: "enable_biometrics_to_unlock_private_space"
+    namespace: "profile_experiences"
+    description: "Add support to unlock the private space using biometrics"
+    bug: "312184187"
+}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 330b992..c0d1fb9 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -131,6 +131,7 @@
     int getUserBadgeDarkColorResId(int userId);
     int getUserStatusBarIconResId(int userId);
     boolean hasBadge(int userId);
+    int getProfileLabelResId(int userId);
     boolean isUserUnlocked(int userId);
     boolean isUserRunning(int userId);
     boolean isUserForeground(int userId);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 08d6e02..ec6d20f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -5693,6 +5693,44 @@
     }
 
     /**
+     * Returns the string/label that should be used to represent the context user. For example,
+     * this string can represent a profile in tabbed views. This is only applicable to
+     * {@link #isProfile() profile users}. This string is translated to the device default language.
+     *
+     * @return String representing the label for the context user.
+     *
+     * @throws android.content.res.Resources.NotFoundException if the user does not have a label
+     * defined.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("UnflaggedApi") // b/306636213
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.QUERY_USERS,
+                    Manifest.permission.INTERACT_ACROSS_USERS})
+    public @NonNull String getProfileLabel() {
+        if (isManagedProfile(mUserId)) {
+            DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+            return dpm.getResources().getString(
+                    android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB,
+                    () -> getDefaultProfileLabel(mUserId));
+        }
+        return getDefaultProfileLabel(mUserId);
+    }
+
+    private String getDefaultProfileLabel(int userId) {
+        try {
+            final int resourceId = mService.getProfileLabelResId(userId);
+            return Resources.getSystem().getString(resourceId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * If the user is a {@link UserManager#isProfile profile}, checks if the user
      * shares media with its parent user (the user that created this profile).
      * Returns false for any other type of user.
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
new file mode 100644
index 0000000..5978383
--- /dev/null
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.service.chooser"
+
+flag {
+  name: "support_nfc_resolver"
+  namespace: "systemui"
+  description: "This flag controls the new NFC 'resolver' activity"
+  bug: "268089816"
+}
+
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 9f886c8..d131dc9 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -69,6 +69,7 @@
     private final String mName;
     private final int mVendorId;
     private final int mProductId;
+    private final int mDeviceBus;
     private final String mDescriptor;
     private final InputDeviceIdentifier mIdentifier;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -468,8 +469,8 @@
      * Called by native code
      */
     private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
-            int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
-            KeyCharacterMap keyCharacterMap, @Nullable String keyboardLanguageTag,
+            int productId, int deviceBus, String descriptor, boolean isExternal, int sources,
+            int keyboardType, KeyCharacterMap keyCharacterMap, @Nullable String keyboardLanguageTag,
             @Nullable String keyboardLayoutType, boolean hasVibrator, boolean hasMicrophone,
             boolean hasButtonUnderPad, boolean hasSensor, boolean hasBattery, int usiVersionMajor,
             int usiVersionMinor, int associatedDisplayId) {
@@ -479,6 +480,7 @@
         mName = name;
         mVendorId = vendorId;
         mProductId = productId;
+        mDeviceBus = deviceBus;
         mDescriptor = descriptor;
         mIsExternal = isExternal;
         mSources = sources;
@@ -512,6 +514,7 @@
         mName = in.readString();
         mVendorId = in.readInt();
         mProductId = in.readInt();
+        mDeviceBus = in.readInt();
         mDescriptor = in.readString();
         mIsExternal = in.readInt() != 0;
         mSources = in.readInt();
@@ -551,6 +554,7 @@
         private String mName = "";
         private int mVendorId = 0;
         private int mProductId = 0;
+        private int mDeviceBus = 0;
         private String mDescriptor = "";
         private boolean mIsExternal = false;
         private int mSources = 0;
@@ -604,6 +608,12 @@
             return this;
         }
 
+        /** @see InputDevice#getDeviceBus() */
+        public Builder setDeviceBus(int deviceBus) {
+            mDeviceBus = deviceBus;
+            return this;
+        }
+
         /** @see InputDevice#getDescriptor() */
         public Builder setDescriptor(String descriptor) {
             mDescriptor = descriptor;
@@ -705,6 +715,7 @@
                     mName,
                     mVendorId,
                     mProductId,
+                    mDeviceBus,
                     mDescriptor,
                     mIsExternal,
                     mSources,
@@ -847,6 +858,21 @@
     }
 
     /**
+     * Gets the device bus used by given device, if available.
+     * <p>
+     * The device bus is the communication system used for transferring data
+     * (e.g. USB, Bluetooth etc.). This value comes from the kernel (from input.h).
+     * A value of 0 will be assigned where the device bus is not available.
+     * </p>
+     *
+     * @return The device bus of a given device
+     * @hide
+     */
+    public int getDeviceBus() {
+        return mDeviceBus;
+    }
+
+    /**
      * Gets the input device descriptor, which is a stable identifier for an input device.
      * <p>
      * An input device descriptor uniquely identifies an input device.  Its value
@@ -1448,6 +1474,7 @@
         out.writeString(mName);
         out.writeInt(mVendorId);
         out.writeInt(mProductId);
+        out.writeInt(mDeviceBus);
         out.writeString(mDescriptor);
         out.writeInt(mIsExternal ? 1 : 0);
         out.writeInt(mSources);
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 542c783c..14ec14b 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -60,6 +60,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.IResultReceiver;
+import com.android.modules.expresslog.Counter;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -68,6 +69,7 @@
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Main session associated with a context.
@@ -81,6 +83,9 @@
 
     private static final String TAG = MainContentCaptureSession.class.getSimpleName();
 
+    private static final String CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID =
+            "content_capture.value_content_capture_wrong_thread_count";
+
     // For readability purposes...
     private static final boolean FORCE_FLUSH = true;
 
@@ -165,6 +170,8 @@
     @Nullable
     private final LocalLog mFlushHistory;
 
+    private final AtomicInteger mWrongThreadCount = new AtomicInteger(0);
+
     /**
      * Binder object used to update the session state.
      */
@@ -701,6 +708,7 @@
                     + getDebugState());
         }
 
+        reportWrongThreadMetric();
         try {
             mSystemServerInterface.finishSession(mId);
         } catch (RemoteException e) {
@@ -1040,20 +1048,27 @@
     }
 
     /**
-     * Checks that the current work is running on the assigned thread from {@code mHandler}.
+     * Checks that the current work is running on the assigned thread from {@code mHandler} and
+     * count the number of times running on the wrong thread.
      *
      * <p>It is not guaranteed that the callers always invoke function from a single thread.
      * Therefore, accessing internal properties in {@link MainContentCaptureSession} should
      * always delegate to the assigned thread from {@code mHandler} for synchronization.</p>
      */
     private void checkOnContentCaptureThread() {
-        // TODO(b/309411951): Add metrics to track the issue instead.
         final boolean onContentCaptureThread = mHandler.getLooper().isCurrentThread();
         if (!onContentCaptureThread) {
+            mWrongThreadCount.incrementAndGet();
             Log.e(TAG, "MainContentCaptureSession running on " + Thread.currentThread());
         }
     }
 
+    /** Reports number of times running on the wrong thread. */
+    private void reportWrongThreadMetric() {
+        Counter.logIncrement(
+                CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID, mWrongThreadCount.getAndSet(0));
+    }
+
     /**
      * Ensures that {@code r} will be running on the assigned thread.
      *
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 1b9ff44..8e89541 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -16,6 +16,8 @@
 
 package android.webkit;
 
+import static android.webkit.Flags.updateServiceV2;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -205,6 +207,9 @@
      * Returns whether WebView should run in multiprocess mode.
      */
     public boolean isMultiProcessEnabled() {
+        if (updateServiceV2()) {
+            return true;
+        }
         try {
             return WebViewFactory.getUpdateService().isMultiProcessEnabled();
         } catch (RemoteException e) {
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index bc7a5fd..e14ae72 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -16,6 +16,8 @@
 
 package android.webkit;
 
+import static android.webkit.Flags.updateServiceV2;
+
 import android.content.pm.PackageInfo;
 import android.os.Build;
 import android.os.ChildZygoteProcess;
@@ -50,8 +52,8 @@
     private static PackageInfo sPackage;
 
     /**
-     * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote
-     * will not be started.
+     * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote will
+     * not be started. Should be removed entirely after we remove the updateServiceV2 flag.
      */
     @GuardedBy("sLock")
     private static boolean sMultiprocessEnabled = false;
@@ -73,11 +75,19 @@
 
     public static boolean isMultiprocessEnabled() {
         synchronized (sLock) {
-            return sMultiprocessEnabled && sPackage != null;
+            if (updateServiceV2()) {
+                return sPackage != null;
+            } else {
+                return sMultiprocessEnabled && sPackage != null;
+            }
         }
     }
 
     public static void setMultiprocessEnabled(boolean enabled) {
+        if (updateServiceV2()) {
+            throw new IllegalStateException(
+                    "setMultiprocessEnabled shouldn't be called if update_service_v2 flag is set.");
+        }
         synchronized (sLock) {
             sMultiprocessEnabled = enabled;
 
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index 35ce726..34c6399 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -19,6 +19,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.graphics.Matrix;
 import android.graphics.Rect;
@@ -87,6 +88,38 @@
         @NonNull
         public final Matrix transform;
 
+        /**
+         * True if the window is touchable.
+         */
+        @SuppressLint("UnflaggedApi") // The API is only used for tests.
+        public final boolean isTouchable;
+
+        /**
+         * True if the window is focusable.
+         */
+        @SuppressLint("UnflaggedApi") // The API is only used for tests.
+        public final boolean isFocusable;
+
+        /**
+         * True if the window is preventing splitting
+         */
+        @SuppressLint("UnflaggedApi") // The API is only used for tests.
+        public final boolean isPreventSplitting;
+
+        /**
+         * True if the window duplicates touches received to wallpaper.
+         */
+        @SuppressLint("UnflaggedApi") // The API is only used for tests.
+        public final boolean isDuplicateTouchToWallpaper;
+
+        /**
+         * True if the window is listening for when there is a touch DOWN event
+         * occurring outside its touchable bounds. When such an event occurs,
+         * this window will receive a MotionEvent with ACTION_OUTSIDE.
+         */
+        @SuppressLint("UnflaggedApi") // The API is only used for tests.
+        public final boolean isWatchOutsideTouch;
+
         WindowInfo(@NonNull IBinder windowToken, @NonNull String name, int displayId,
                 @NonNull Rect bounds, int inputConfig, @NonNull Matrix transform) {
             this.windowToken = windowToken;
@@ -96,6 +129,14 @@
             this.isTrustedOverlay = (inputConfig & InputConfig.TRUSTED_OVERLAY) != 0;
             this.isVisible = (inputConfig & InputConfig.NOT_VISIBLE) == 0;
             this.transform = transform;
+            this.isTouchable = (inputConfig & InputConfig.NOT_TOUCHABLE) == 0;
+            this.isFocusable = (inputConfig & InputConfig.NOT_FOCUSABLE) == 0;
+            this.isPreventSplitting = (inputConfig
+                            & InputConfig.PREVENT_SPLITTING) != 0;
+            this.isDuplicateTouchToWallpaper = (inputConfig
+                            & InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER) != 0;
+            this.isWatchOutsideTouch = (inputConfig
+                            & InputConfig.WATCH_OUTSIDE_TOUCH) != 0;
         }
 
         @Override
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 7f93213..29932f3 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -48,3 +48,11 @@
     is_fixed_read_only: true
     bug: "262477923"
 }
+
+flag {
+    namespace: "window_surfaces"
+    name: "secure_window_state"
+    description: "Move SC secure flag to WindowState level"
+    is_fixed_read_only: true
+    bug: "308662081"
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 4705dc5..31a3ebd 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -8,6 +8,13 @@
 }
 
 flag {
+  name: "edge_to_edge_by_default"
+  namespace: "windowing_frontend"
+  description: "Make app go edge-to-edge by default when targeting SDK 35 or greater"
+  bug: "309578419"
+}
+
+flag {
     name: "defer_display_updates"
     namespace: "window_manager"
     description: "Feature flag for deferring DisplayManager updates to WindowManager if Shell transition is running"
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageHidden.java b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageHidden.java
similarity index 86%
rename from services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageHidden.java
rename to core/java/com/android/internal/pm/parsing/pkg/AndroidPackageHidden.java
index 1fafdf9..0d7b433 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageHidden.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageHidden.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.parsing.pkg;
+package com.android.internal.pm.parsing.pkg;
 
 import android.annotation.Nullable;
 import android.content.pm.ApplicationInfo;
@@ -22,16 +22,18 @@
 
 /**
  * Methods that normal consumers should not have access to. This usually means the field is stateful
- * or deprecated and should be access through {@link AndroidPackageUtils} or a system manager
- * class.
+ * or deprecated and should be access through
+ * {@link com.android.server.pm.parsing.pkg.AndroidPackageUtils} or a system manager class.
  * <p>
  * This is a separate interface, not implemented by the base {@link AndroidPackage} because Java
  * doesn't support non-public interface methods. The class must be cast to this interface.
  * <p>
  * Because they exist in different packages, some methods are duplicated from
  * android.content.pm.parsing.ParsingPackageHidden.
+ * @hide
  */
-interface AndroidPackageHidden {
+// TODO: remove public after moved PackageImpl and AndroidPackageUtils
+public interface AndroidPackageHidden {
 
     /**
      * @see ApplicationInfo#primaryCpuAbi
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageInternal.java b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageInternal.java
similarity index 96%
rename from services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageInternal.java
rename to core/java/com/android/internal/pm/parsing/pkg/AndroidPackageInternal.java
index 9eca7d6..6f8e658 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageInternal.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageInternal.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.parsing.pkg;
+package com.android.internal.pm.parsing.pkg;
 
 import android.annotation.NonNull;
 
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java b/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
similarity index 98%
rename from services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
rename to core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
index 85f8f76..7ef0b48 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.parsing.pkg;
+package com.android.internal.pm.parsing.pkg;
 
 import android.content.pm.SigningDetails;
 
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java b/core/java/com/android/internal/pm/pkg/AndroidPackageSplitImpl.java
similarity index 94%
rename from services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java
rename to core/java/com/android/internal/pm/pkg/AndroidPackageSplitImpl.java
index c0f2c25..3c564e9 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java
+++ b/core/java/com/android/internal/pm/pkg/AndroidPackageSplitImpl.java
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg;
+package com.android.internal.pm.pkg;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.ApplicationInfo;
 
+import com.android.server.pm.pkg.AndroidPackageSplit;
+
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -94,8 +96,8 @@
         if (this == o) return true;
         if (!(o instanceof AndroidPackageSplitImpl)) return false;
         AndroidPackageSplitImpl that = (AndroidPackageSplitImpl) o;
-        var fieldsEqual = mRevisionCode == that.mRevisionCode && mFlags == that.mFlags && Objects.equals(
-                mName, that.mName) && Objects.equals(mPath, that.mPath)
+        var fieldsEqual = mRevisionCode == that.mRevisionCode && mFlags == that.mFlags
+                && Objects.equals(mName, that.mName) && Objects.equals(mPath, that.mPath)
                 && Objects.equals(mClassLoaderName, that.mClassLoaderName);
 
         if (!fieldsEqual) return false;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
similarity index 98%
rename from services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
rename to core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
index 099c676..4ed361f 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.parsing;
+package com.android.internal.pm.pkg.parsing;
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
@@ -32,6 +32,7 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.R;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedApexSystemService;
 import com.android.internal.pm.pkg.component.ParsedAttribution;
@@ -43,7 +44,6 @@
 import com.android.internal.pm.pkg.component.ParsedProvider;
 import com.android.internal.pm.pkg.component.ParsedService;
 import com.android.internal.pm.pkg.component.ParsedUsesPermission;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 
 import java.security.PublicKey;
 import java.util.List;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageHidden.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageHidden.java
similarity index 96%
rename from services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageHidden.java
rename to core/java/com/android/internal/pm/pkg/parsing/ParsingPackageHidden.java
index 9c1c9ac..5758fd7 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageHidden.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageHidden.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.parsing;
+package com.android.internal.pm.pkg.parsing;
 
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 942aa8c..dd310dc 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -175,6 +175,14 @@
     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
     private static final long NAV_BAR_COLOR_DEFAULT_TRANSPARENT = 232195501L;
 
+    /**
+     * Make app go edge-to-edge by default if the target SDK is
+     * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static final long EDGE_TO_EDGE_BY_DEFAULT = 309578419;
+
     private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |
             (1 << FEATURE_CUSTOM_TITLE) |
             (1 << FEATURE_CONTENT_TRANSITIONS) |
@@ -387,7 +395,9 @@
         mAllowFloatingWindowsFillScreen = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowFloatingWindowsFillScreen);
         mDefaultEdgeToEdge =
-                context.getApplicationInfo().targetSdkVersion >= DEFAULT_EDGE_TO_EDGE_SDK_VERSION;
+                context.getApplicationInfo().targetSdkVersion >= DEFAULT_EDGE_TO_EDGE_SDK_VERSION
+                        || (CompatChanges.isChangeEnabled(EDGE_TO_EDGE_BY_DEFAULT)
+                                && Flags.edgeToEdgeByDefault());
         if (mDefaultEdgeToEdge) {
             mDecorFitsSystemWindows = false;
         }
@@ -2558,11 +2568,11 @@
 
             mNavigationBarColor =
                     navBarColor == navBarDefaultColor
+                            && !mDefaultEdgeToEdge
                             && !(CompatChanges.isChangeEnabled(NAV_BAR_COLOR_DEFAULT_TRANSPARENT)
                                     && Flags.navBarTransparentByDefault())
                             && !context.getResources().getBoolean(
                                     R.bool.config_navBarDefaultTransparent)
-                            && !mDefaultEdgeToEdge
                     ? navBarCompatibleColor
                     : navBarColor;
 
diff --git a/core/java/com/android/server/pm/OWNERS b/core/java/com/android/server/pm/OWNERS
new file mode 100644
index 0000000..6ef34e2
--- /dev/null
+++ b/core/java/com/android/server/pm/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36137
+
+file:/PACKAGE_MANAGER_OWNERS
+
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java
similarity index 98%
rename from services/core/java/com/android/server/pm/pkg/AndroidPackage.java
rename to core/java/com/android/server/pm/pkg/AndroidPackage.java
index 99819c8..4e4f26c 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -58,7 +58,6 @@
 import com.android.internal.pm.pkg.component.ParsedProvider;
 import com.android.internal.pm.pkg.component.ParsedService;
 import com.android.internal.pm.pkg.component.ParsedUsesPermission;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 import java.security.PublicKey;
 import java.util.List;
@@ -691,7 +690,7 @@
 
     /**
      * The names of packages to adopt ownership of permissions from, parsed under {@link
-     * ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
+     * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
      *
      * @see R.styleable#AndroidManifestOriginalPackage_name
      * @hide
@@ -796,7 +795,7 @@
 
     /**
      * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link
-     * ParsingPackageUtils#TAG_KEY_SETS}.
+     * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_KEY_SETS}.
      *
      * @see R.styleable#AndroidManifestKeySet
      * @see R.styleable#AndroidManifestPublicKey
@@ -905,7 +904,7 @@
      * For system use to migrate from an old package name to a new one, moving over data if
      * available.
      *
-     * @see R.styleable#AndroidManifestOriginalPackage}
+     * @see R.styleable#AndroidManifestOriginalPackage
      * @hide
      */
     @NonNull
@@ -1267,7 +1266,7 @@
 
     /**
      * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link
-     * ParsingPackageUtils#TAG_KEY_SETS}.
+     * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_KEY_SETS}.
      *
      * @see R.styleable#AndroidManifestUpgradeKeySet
      * @hide
@@ -1417,6 +1416,7 @@
      * @see ApplicationInfo#FLAG_IS_GAME
      * @see R.styleable#AndroidManifestApplication_isGame
      * @hide
+     * @deprecated
      */
     @Deprecated
     boolean isGame();
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java b/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java
similarity index 100%
rename from services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java
rename to core/java/com/android/server/pm/pkg/AndroidPackageSplit.java
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 262f5e8..239c626 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -81,7 +81,8 @@
                                           deviceInfo.getId(), deviceInfo.getGeneration(),
                                           deviceInfo.getControllerNumber(), nameObj.get(),
                                           static_cast<int32_t>(ident.vendor),
-                                          static_cast<int32_t>(ident.product), descriptorObj.get(),
+                                          static_cast<int32_t>(ident.product),
+                                          static_cast<int32_t>(ident.bus), descriptorObj.get(),
                                           deviceInfo.isExternal(), deviceInfo.getSources(),
                                           deviceInfo.getKeyboardType(), kcmObj.get(),
                                           keyboardLanguageTagObj.get(), keyboardLayoutTypeObj.get(),
@@ -111,7 +112,7 @@
     gInputDeviceClassInfo.clazz = MakeGlobalRefOrDie(env, gInputDeviceClassInfo.clazz);
 
     gInputDeviceClassInfo.ctor = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
-                                                  "(IIILjava/lang/String;IILjava/lang/"
+                                                  "(IIILjava/lang/String;IIILjava/lang/"
                                                   "String;ZIILandroid/view/KeyCharacterMap;Ljava/"
                                                   "lang/String;Ljava/lang/String;ZZZZZIII)V");
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index eed186a..73a7e42 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6350,4 +6350,20 @@
     <string name="keyboard_layout_notification_multiple_selected_title">Physical keyboards configured</string>
     <!-- Notification message when multiple keyboards with selected layouts have been connected the first time simultaneously [CHAR LIMIT=NOTIF_BODY] -->
     <string name="keyboard_layout_notification_multiple_selected_message">Tap to view keyboards</string>
+
+    <!-- Private profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+    <string name="profile_label_private">Private</string>
+    <!-- Clone profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+    <string name="profile_label_clone">Clone</string>
+    <!-- Work profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+    <string name="profile_label_work">Work</string>
+    <!-- 2nd Work profile label on a screen in case a device has more than one work profiles. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+    <string name="profile_label_work_2">Work 2</string>
+    <!-- 3rd Work profile label on a screen in case a device has more than two work profiles. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+    <string name="profile_label_work_3">Work 3</string>
+    <!-- Test profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20]  -->
+    <string name="profile_label_test">Test</string>
+    <!-- Communal profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+    <string name="profile_label_communal">Communal</string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 24b39bc..38f1f67 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1086,6 +1086,13 @@
   <java-symbol type="string" name="managed_profile_label_badge_3" />
   <java-symbol type="string" name="clone_profile_label_badge" />
   <java-symbol type="string" name="private_profile_label_badge" />
+  <java-symbol type="string" name="profile_label_private" />
+  <java-symbol type="string" name="profile_label_clone" />
+  <java-symbol type="string" name="profile_label_work" />
+  <java-symbol type="string" name="profile_label_work_2" />
+  <java-symbol type="string" name="profile_label_work_3" />
+  <java-symbol type="string" name="profile_label_test" />
+  <java-symbol type="string" name="profile_label_communal" />
   <java-symbol type="string" name="mediasize_unknown_portrait" />
   <java-symbol type="string" name="mediasize_unknown_landscape" />
   <java-symbol type="string" name="mediasize_iso_a0" />
diff --git a/core/tests/timetests/TEST_MAPPING b/core/tests/timetests/TEST_MAPPING
index 5748044..0796d5a 100644
--- a/core/tests/timetests/TEST_MAPPING
+++ b/core/tests/timetests/TEST_MAPPING
@@ -1,6 +1,5 @@
 {
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FrameworksTimeCoreTests"
     }
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index f19acbe..d36ac39 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -991,12 +991,6 @@
       "group": "WM_DEBUG_WINDOW_INSETS",
       "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
     },
-    "-1176488860": {
-      "message": "SURFACE isSecure=%b: %s",
-      "level": "INFO",
-      "group": "WM_SHOW_TRANSACTIONS",
-      "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
-    },
     "-1164930508": {
       "message": "Moving to RESUMED: %s (starting new instance) callers=%s",
       "level": "VERBOSE",
@@ -3277,6 +3271,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "810599500": {
+      "message": "SURFACE isSecure=%b: %s",
+      "level": "INFO",
+      "group": "WM_SHOW_TRANSACTIONS",
+      "at": "com\/android\/server\/wm\/WindowState.java"
+    },
     "829434921": {
       "message": "Draw state now committed in %s",
       "level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 3e11327..e74e578d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -788,7 +788,7 @@
                 mLayerView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
                     if (!windowInsets.equals(mWindowInsets) && mLayerView != null) {
                         mWindowInsets = windowInsets;
-                        mBubblePositioner.update();
+                        mBubblePositioner.update(DeviceConfig.create(mContext, mWindowManager));
                         mLayerView.onDisplaySizeChanged();
                     }
                     return windowInsets;
@@ -798,7 +798,7 @@
                 mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
                     if (!windowInsets.equals(mWindowInsets) && mStackView != null) {
                         mWindowInsets = windowInsets;
-                        mBubblePositioner.update();
+                        mBubblePositioner.update(DeviceConfig.create(mContext, mWindowManager));
                         mStackView.onDisplaySizeChanged();
                     }
                     return windowInsets;
@@ -980,7 +980,7 @@
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         if (mBubblePositioner != null) {
-            mBubblePositioner.update();
+            mBubblePositioner.update(DeviceConfig.create(mContext, mWindowManager));
         }
         if (mStackView != null && newConfig != null) {
             if (newConfig.densityDpi != mDensityDpi
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 09ae84a..1efd9df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -16,10 +16,7 @@
 
 package com.android.wm.shell.bubbles;
 
-import static android.view.View.LAYOUT_DIRECTION_RTL;
-
 import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Insets;
 import android.graphics.Point;
@@ -28,9 +25,7 @@
 import android.graphics.RectF;
 import android.util.Log;
 import android.view.Surface;
-import android.view.WindowInsets;
 import android.view.WindowManager;
-import android.view.WindowMetrics;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -68,15 +63,12 @@
     private static final float EXPANDED_VIEW_BUBBLE_BAR_LANDSCAPE_WIDTH_PERCENT = 0.4f;
 
     private Context mContext;
-    private WindowManager mWindowManager;
+    private DeviceConfig mDeviceConfig;
     private Rect mScreenRect;
     private @Surface.Rotation int mRotation = Surface.ROTATION_0;
     private Insets mInsets;
     private boolean mImeVisible;
     private int mImeHeight;
-    private boolean mIsLargeScreen;
-    private boolean mIsSmallTablet;
-
     private Rect mPositionRect;
     private int mDefaultMaxBubbles;
     private int mMaxBubbles;
@@ -110,44 +102,27 @@
 
     public BubblePositioner(Context context, WindowManager windowManager) {
         mContext = context;
-        mWindowManager = windowManager;
-        update();
+        mDeviceConfig = DeviceConfig.create(context, windowManager);
+        update(mDeviceConfig);
     }
 
     /**
      * Available space and inset information. Call this when config changes
      * occur or when added to a window.
      */
-    public void update() {
-        WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
-        if (windowMetrics == null) {
-            return;
-        }
-        WindowInsets metricInsets = windowMetrics.getWindowInsets();
-        Insets insets = metricInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
-                | WindowInsets.Type.statusBars()
-                | WindowInsets.Type.displayCutout());
-
-        final Rect bounds = windowMetrics.getBounds();
-        Configuration config = mContext.getResources().getConfiguration();
-        mIsLargeScreen = config.smallestScreenWidthDp >= 600;
-        if (mIsLargeScreen) {
-            float largestEdgeDp = Math.max(config.screenWidthDp, config.screenHeightDp);
-            mIsSmallTablet = largestEdgeDp < 960;
-        } else {
-            mIsSmallTablet = false;
-        }
+    public void update(DeviceConfig deviceConfig) {
+        mDeviceConfig = deviceConfig;
 
         if (BubbleDebugConfig.DEBUG_POSITIONER) {
             Log.w(TAG, "update positioner:"
                     + " rotation: " + mRotation
-                    + " insets: " + insets
-                    + " isLargeScreen: " + mIsLargeScreen
-                    + " isSmallTablet: " + mIsSmallTablet
+                    + " insets: " + deviceConfig.getInsets()
+                    + " isLargeScreen: " + deviceConfig.isLargeScreen()
+                    + " isSmallTablet: " + deviceConfig.isSmallTablet()
                     + " showingInBubbleBar: " + mShowingInBubbleBar
-                    + " bounds: " + bounds);
+                    + " bounds: " + deviceConfig.getWindowBounds());
         }
-        updateInternal(mRotation, insets, bounds);
+        updateInternal(mRotation, deviceConfig.getInsets(), deviceConfig.getWindowBounds());
     }
 
     @VisibleForTesting
@@ -175,15 +150,15 @@
             mExpandedViewLargeScreenWidth = isLandscape()
                     ? (int) (bounds.width() * EXPANDED_VIEW_BUBBLE_BAR_LANDSCAPE_WIDTH_PERCENT)
                     : (int) (bounds.width() * EXPANDED_VIEW_BUBBLE_BAR_PORTRAIT_WIDTH_PERCENT);
-        } else if (mIsSmallTablet) {
+        } else if (mDeviceConfig.isSmallTablet()) {
             mExpandedViewLargeScreenWidth = (int) (bounds.width()
                     * EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT);
         } else {
             mExpandedViewLargeScreenWidth =
                     res.getDimensionPixelSize(R.dimen.bubble_expanded_view_largescreen_width);
         }
-        if (mIsLargeScreen) {
-            if (mIsSmallTablet) {
+        if (mDeviceConfig.isLargeScreen()) {
+            if (mDeviceConfig.isSmallTablet()) {
                 final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2;
                 mExpandedViewLargeScreenInsetClosestEdge = centeredInset;
                 mExpandedViewLargeScreenInsetFurthestEdge = centeredInset;
@@ -264,13 +239,12 @@
 
     /** @return whether the device is in landscape orientation. */
     public boolean isLandscape() {
-        return mContext.getResources().getConfiguration().orientation
-                == Configuration.ORIENTATION_LANDSCAPE;
+        return mDeviceConfig.isLandscape();
     }
 
     /** @return whether the screen is considered large. */
     public boolean isLargeScreen() {
-        return mIsLargeScreen;
+        return mDeviceConfig.isLargeScreen();
     }
 
     /**
@@ -281,7 +255,7 @@
      * to the left or right side.
      */
     public boolean showBubblesVertically() {
-        return isLandscape() || mIsLargeScreen;
+        return isLandscape() || mDeviceConfig.isLargeScreen();
     }
 
     /** Size of the bubble. */
@@ -334,7 +308,7 @@
     }
 
     private int getExpandedViewLargeScreenInsetFurthestEdge(boolean isOverflow) {
-        if (isOverflow && mIsLargeScreen) {
+        if (isOverflow && mDeviceConfig.isLargeScreen()) {
             return mScreenRect.width()
                     - mExpandedViewLargeScreenInsetClosestEdge
                     - mOverflowWidth;
@@ -358,7 +332,7 @@
         final int pointerTotalHeight = getPointerSize();
         final int expandedViewLargeScreenInsetFurthestEdge =
                 getExpandedViewLargeScreenInsetFurthestEdge(isOverflow);
-        if (mIsLargeScreen) {
+        if (mDeviceConfig.isLargeScreen()) {
             // Note:
             // If we're in portrait OR if we're a small tablet, then the two insets values will
             // be equal. If we're landscape and a large tablet, the two values will be different.
@@ -439,12 +413,12 @@
      */
     public float getExpandedViewHeight(BubbleViewProvider bubble) {
         boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
-        if (isOverflow && showBubblesVertically() && !mIsLargeScreen) {
+        if (isOverflow && showBubblesVertically() && !mDeviceConfig.isLargeScreen()) {
             // overflow in landscape on phone is max
             return MAX_HEIGHT;
         }
 
-        if (mIsLargeScreen && !mIsSmallTablet && !isOverflow) {
+        if (mDeviceConfig.isLargeScreen() && !mDeviceConfig.isSmallTablet() && !isOverflow) {
             // the expanded view height on large tablets is calculated based on the shortest screen
             // size and is the same in both portrait and landscape
             int maxVerticalInset = Math.max(mInsets.top, mInsets.bottom);
@@ -529,11 +503,9 @@
      */
     public PointF getExpandedBubbleXY(int index, BubbleStackView.StackViewState state) {
         boolean showBubblesVertically = showBubblesVertically();
-        boolean isRtl = mContext.getResources().getConfiguration().getLayoutDirection()
-                == LAYOUT_DIRECTION_RTL;
 
         int onScreenIndex;
-        if (showBubblesVertically || !isRtl) {
+        if (showBubblesVertically || !mDeviceConfig.isRtl()) {
             onScreenIndex = index;
         } else {
             // If bubbles are shown horizontally, check if RTL language is used.
@@ -554,10 +526,10 @@
         if (showBubblesVertically) {
             int inset = mExpandedViewLargeScreenInsetClosestEdge;
             y = rowStart + positionInRow;
-            int left = mIsLargeScreen
+            int left = mDeviceConfig.isLargeScreen()
                     ? inset - mExpandedViewPadding - mBubbleSize
                     : mPositionRect.left;
-            int right = mIsLargeScreen
+            int right = mDeviceConfig.isLargeScreen()
                     ? mPositionRect.right - inset + mExpandedViewPadding
                     : mPositionRect.right - mBubbleSize;
             x = state.onLeft
@@ -693,13 +665,10 @@
      * @param isAppBubble whether this start position is for an app bubble or not.
      */
     public PointF getDefaultStartPosition(boolean isAppBubble) {
-        final int layoutDirection = mContext.getResources().getConfiguration().getLayoutDirection();
         // Normal bubbles start on the left if we're in LTR, right otherwise.
         // TODO (b/294284894): update language around "app bubble" here
         // App bubbles start on the right in RTL, left otherwise.
-        final boolean startOnLeft = isAppBubble
-                ? layoutDirection == LAYOUT_DIRECTION_RTL
-                : layoutDirection != LAYOUT_DIRECTION_RTL;
+        final boolean startOnLeft = isAppBubble ? mDeviceConfig.isRtl() : !mDeviceConfig.isRtl();
         return getStartPosition(startOnLeft ? StackPinnedEdge.LEFT : StackPinnedEdge.RIGHT);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 2cee675..8f904c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -61,6 +61,7 @@
 import android.view.ViewOutlineProvider;
 import android.view.ViewPropertyAnimator;
 import android.view.ViewTreeObserver;
+import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -1001,7 +1002,8 @@
 
         mOrientationChangedListener =
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                    mPositioner.update();
+                    mPositioner.update(DeviceConfig.create(mContext, mContext.getSystemService(
+                            WindowManager.class)));
                     onDisplaySizeChanged();
                     mExpandedAnimationController.updateResources();
                     mStackAnimationController.updateResources();
@@ -1522,7 +1524,8 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mPositioner.update();
+        WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+        mPositioner.update(DeviceConfig.create(mContext, Objects.requireNonNull(windowManager)));
         getViewTreeObserver().addOnComputeInternalInsetsListener(this);
         getViewTreeObserver().addOnDrawListener(mSystemGestureExcludeUpdater);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DeviceConfig.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DeviceConfig.kt
new file mode 100644
index 0000000..9293309
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DeviceConfig.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles
+
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.graphics.Insets
+import android.graphics.Rect
+import android.view.View.LAYOUT_DIRECTION_RTL
+import android.view.WindowInsets
+import android.view.WindowManager
+import kotlin.math.max
+
+/** Contains device configuration used for positioning bubbles on the screen. */
+data class DeviceConfig(
+        val isLargeScreen: Boolean,
+        val isSmallTablet: Boolean,
+        val isLandscape: Boolean,
+        val isRtl: Boolean,
+        val windowBounds: Rect,
+        val insets: Insets
+) {
+    companion object {
+
+        private const val LARGE_SCREEN_MIN_EDGE_DP = 600
+        private const val SMALL_TABLET_MAX_EDGE_DP = 960
+
+        @JvmStatic
+        fun create(context: Context, windowManager: WindowManager): DeviceConfig {
+            val windowMetrics = windowManager.currentWindowMetrics
+            val metricInsets = windowMetrics.windowInsets
+            val insets = metricInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
+                    or WindowInsets.Type.statusBars()
+                    or WindowInsets.Type.displayCutout())
+            val windowBounds = windowMetrics.bounds
+            val config: Configuration = context.resources.configuration
+            val isLargeScreen = config.smallestScreenWidthDp >= LARGE_SCREEN_MIN_EDGE_DP
+            val largestEdgeDp = max(config.screenWidthDp, config.screenHeightDp)
+            val isSmallTablet = isLargeScreen && largestEdgeDp < SMALL_TABLET_MAX_EDGE_DP
+            val isLandscape = context.resources.configuration.orientation == ORIENTATION_LANDSCAPE
+            val isRtl = context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
+            return DeviceConfig(
+                    isLargeScreen = isLargeScreen,
+                    isSmallTablet = isSmallTablet,
+                    isLandscape = isLandscape,
+                    isRtl = isRtl,
+                    windowBounds = windowBounds,
+                    insets = insets
+            )
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index e788341..92cb436 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -28,13 +28,16 @@
 import android.view.TouchDelegate;
 import android.view.View;
 import android.view.ViewTreeObserver;
+import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 import com.android.wm.shell.bubbles.BubbleController;
 import com.android.wm.shell.bubbles.BubbleOverflow;
 import com.android.wm.shell.bubbles.BubblePositioner;
 import com.android.wm.shell.bubbles.BubbleViewProvider;
+import com.android.wm.shell.bubbles.DeviceConfig;
 
+import java.util.Objects;
 import java.util.function.Consumer;
 
 import kotlin.Unit;
@@ -104,7 +107,8 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mPositioner.update();
+        WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+        mPositioner.update(DeviceConfig.create(mContext, Objects.requireNonNull(windowManager)));
         getViewTreeObserver().addOnComputeInternalInsetsListener(this);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 4a9ea6f..144555d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -21,7 +21,6 @@
 import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
-import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
 import android.app.WindowConfiguration.WindowingMode
 import android.content.Context
@@ -321,24 +320,10 @@
     }
 
     /** Move a task with given `taskId` to fullscreen */
-    fun moveToFullscreen(taskId: Int) {
-        shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToFullscreen(task) }
-    }
-
-    /** Move a task to fullscreen */
-    fun moveToFullscreen(task: RunningTaskInfo) {
-        KtProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: moveToFullscreen taskId=%d",
-            task.taskId
-        )
-
-        val wct = WindowContainerTransaction()
-        addMoveToFullscreenChanges(wct, task)
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
-        } else {
-            shellTaskOrganizer.applyTransaction(wct)
+    fun moveToFullscreen(taskId: Int, windowDecor: DesktopModeWindowDecoration) {
+        shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task ->
+            windowDecor.incrementRelayoutBlock()
+            moveToFullscreenWithAnimation(task, task.positionInParent)
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index dd6ca8d..03006f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -428,7 +428,8 @@
                 if (isTaskInSplitScreen(mTaskId)) {
                     mSplitScreenController.moveTaskToFullscreen(mTaskId);
                 } else {
-                    mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId));
+                    mDesktopTasksController.ifPresent(c ->
+                            c.moveToFullscreen(mTaskId, mWindowDecorByTaskId.get(mTaskId)));
                 }
             } else if (id == R.id.split_screen_button) {
                 decoration.closeHandleMenu();
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
index 744e8c2..181474f 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
@@ -57,25 +57,18 @@
         resetLetterboxStyle()
         _letterboxStyle = mapLetterboxStyle()
         val isLetterboxEducationEnabled = _letterboxStyle.getValue("Is education enabled")
-        var hasLetterboxEducationStateChanged = false
         if ("$withLetterboxEducationEnabled" != isLetterboxEducationEnabled) {
-            hasLetterboxEducationStateChanged = true
             execAdb("wm set-letterbox-style --isEducationEnabled " + withLetterboxEducationEnabled)
         }
-        return try {
-            object : Statement() {
-                @Throws(Throwable::class)
-                override fun evaluate() {
+        return object : Statement() {
+            @Throws(Throwable::class)
+            override fun evaluate() {
+                try {
                     base!!.evaluate()
+                } finally {
+                    resetLetterboxStyle()
                 }
             }
-        } finally {
-            if (hasLetterboxEducationStateChanged) {
-                execAdb(
-                    "wm set-letterbox-style --isEducationEnabled " + isLetterboxEducationEnabled
-                )
-            }
-            resetLetterboxStyle()
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
index 89ecc29..6429b00 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
@@ -76,6 +76,17 @@
                 value="appops set com.android.shell android:mock_location deny"/>
     </target_preparer>
 
+    <!-- Use app crawler to log into Netflix -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command"
+                value="am start -n com.netflix.mediaclient/com.netflix.mediaclient.ui.login.LoginActivity"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="set-option" value="package-name:com.netflix.mediaclient"/>
+        <option name="set-option" value="ui-automator-mode:true"/>
+        <option name="class" value="com.android.csuite.tests.AppCrawlTest" />
+    </test>
+
     <!-- Needed for pushing the trace config file -->
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
index fdda597..05f937a 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
@@ -95,6 +95,8 @@
         <option name="pull-pattern-keys" value="perfetto_file_path"/>
         <option name="directory-keys"
                 value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
         <option name="collect-on-run-ended-only" value="true"/>
         <option name="clean-up" value="true"/>
     </metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
index b55f4ec..67316d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
@@ -63,6 +63,7 @@
       atrace_categories: "sched_process_exit"
       atrace_apps: "com.android.server.wm.flicker.testapp"
       atrace_apps: "com.android.systemui"
+      atrace_apps: "com.android.wm.shell.flicker.service"
       atrace_apps: "com.android.wm.shell.flicker.splitscreen"
       atrace_apps: "com.google.android.apps.nexuslauncher"
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 26c7394..4bca96b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -192,7 +192,7 @@
                 mMainExecutor);
 
         mPositioner = new TestableBubblePositioner(mContext,
-                mock(WindowManager.class));
+                mContext.getSystemService(WindowManager.class));
         mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner, mEducationController,
                 mMainExecutor);
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
index cb29a21..f5b0174 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
@@ -53,7 +53,8 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        mPositioner = new TestableBubblePositioner(mContext, mock(WindowManager.class));
+        mPositioner = new TestableBubblePositioner(mContext,
+                mContext.getSystemService(WindowManager.class));
         when(mBubbleController.getPositioner()).thenReturn(mPositioner);
         when(mBubbleController.getStackView()).thenReturn(mock(BubbleStackView.class));
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
index 287a97c..835ebe2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
@@ -16,33 +16,17 @@
 
 package com.android.wm.shell.bubbles;
 
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.View.LAYOUT_DIRECTION_LTR;
-import static android.view.View.LAYOUT_DIRECTION_RTL;
-
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
 
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableResources;
-import android.util.DisplayMetrics;
-import android.view.WindowInsets;
 import android.view.WindowManager;
-import android.view.WindowMetrics;
 
 import androidx.test.filters.SmallTest;
 
@@ -52,36 +36,20 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 /**
  * Tests operations and the resulting state managed by {@link BubblePositioner}.
  */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class BubblePositionerTest extends ShellTestCase {
 
-    private static final int MIN_WIDTH_FOR_TABLET = 600;
-
     private BubblePositioner mPositioner;
-    private Configuration mConfiguration;
-
-    @Mock
-    private WindowManager mWindowManager;
-    @Mock
-    private WindowMetrics mWindowMetrics;
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mConfiguration = spy(new Configuration());
-        TestableResources testableResources = mContext.getOrCreateTestableResources();
-        testableResources.overrideConfiguration(mConfiguration);
-
-        mPositioner = new BubblePositioner(mContext, mWindowManager);
+        WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+        mPositioner = new BubblePositioner(mContext, windowManager);
     }
 
     @Test
@@ -91,11 +59,11 @@
         Rect availableRect = new Rect(screenBounds);
         availableRect.inset(insets);
 
-        new WindowManagerConfig()
+        DeviceConfig deviceConfig = new ConfigBuilder()
                 .setInsets(insets)
                 .setScreenBounds(screenBounds)
-                .setUpConfig();
-        mPositioner.update();
+                .build();
+        mPositioner.update(deviceConfig);
 
         assertThat(mPositioner.getAvailableRect()).isEqualTo(availableRect);
         assertThat(mPositioner.isLandscape()).isFalse();
@@ -105,16 +73,16 @@
 
     @Test
     public void testShowBubblesVertically_phonePortrait() {
-        new WindowManagerConfig().setOrientation(ORIENTATION_PORTRAIT).setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().build();
+        mPositioner.update(deviceConfig);
 
         assertThat(mPositioner.showBubblesVertically()).isFalse();
     }
 
     @Test
     public void testShowBubblesVertically_phoneLandscape() {
-        new WindowManagerConfig().setOrientation(ORIENTATION_LANDSCAPE).setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().setLandscape().build();
+        mPositioner.update(deviceConfig);
 
         assertThat(mPositioner.isLandscape()).isTrue();
         assertThat(mPositioner.showBubblesVertically()).isTrue();
@@ -122,8 +90,8 @@
 
     @Test
     public void testShowBubblesVertically_tablet() {
-        new WindowManagerConfig().setLargeScreen().setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build();
+        mPositioner.update(deviceConfig);
 
         assertThat(mPositioner.showBubblesVertically()).isTrue();
     }
@@ -131,8 +99,8 @@
     /** If a resting position hasn't been set, calling it will return the default position. */
     @Test
     public void testGetRestingPosition_returnsDefaultPosition() {
-        new WindowManagerConfig().setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().build();
+        mPositioner.update(deviceConfig);
 
         PointF restingPosition = mPositioner.getRestingPosition();
         PointF defaultPosition = mPositioner.getDefaultStartPosition();
@@ -143,8 +111,8 @@
     /** If a resting position has been set, it'll return that instead of the default position. */
     @Test
     public void testGetRestingPosition_returnsRestingPosition() {
-        new WindowManagerConfig().setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().build();
+        mPositioner.update(deviceConfig);
 
         PointF restingPosition = new PointF(100, 100);
         mPositioner.setRestingPosition(restingPosition);
@@ -155,8 +123,8 @@
     /** Test that the default resting position on phone is in upper left. */
     @Test
     public void testGetRestingPosition_bubble_onPhone() {
-        new WindowManagerConfig().setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().build();
+        mPositioner.update(deviceConfig);
 
         RectF allowableStackRegion =
                 mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -168,8 +136,8 @@
 
     @Test
     public void testGetRestingPosition_bubble_onPhone_RTL() {
-        new WindowManagerConfig().setLayoutDirection(LAYOUT_DIRECTION_RTL).setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().setRtl().build();
+        mPositioner.update(deviceConfig);
 
         RectF allowableStackRegion =
                 mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -182,8 +150,8 @@
     /** Test that the default resting position on tablet is middle left. */
     @Test
     public void testGetRestingPosition_chatBubble_onTablet() {
-        new WindowManagerConfig().setLargeScreen().setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build();
+        mPositioner.update(deviceConfig);
 
         RectF allowableStackRegion =
                 mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -195,9 +163,8 @@
 
     @Test
     public void testGetRestingPosition_chatBubble_onTablet_RTL() {
-        new WindowManagerConfig().setLargeScreen().setLayoutDirection(
-                LAYOUT_DIRECTION_RTL).setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
+        mPositioner.update(deviceConfig);
 
         RectF allowableStackRegion =
                 mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -210,8 +177,8 @@
     /** Test that the default resting position on tablet is middle right. */
     @Test
     public void testGetDefaultPosition_appBubble_onTablet() {
-        new WindowManagerConfig().setLargeScreen().setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build();
+        mPositioner.update(deviceConfig);
 
         RectF allowableStackRegion =
                 mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -223,9 +190,8 @@
 
     @Test
     public void testGetRestingPosition_appBubble_onTablet_RTL() {
-        new WindowManagerConfig().setLargeScreen().setLayoutDirection(
-                LAYOUT_DIRECTION_RTL).setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
+        mPositioner.update(deviceConfig);
 
         RectF allowableStackRegion =
                 mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -237,9 +203,8 @@
 
     @Test
     public void testHasUserModifiedDefaultPosition_false() {
-        new WindowManagerConfig().setLargeScreen().setLayoutDirection(
-                LAYOUT_DIRECTION_RTL).setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
+        mPositioner.update(deviceConfig);
 
         assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
 
@@ -250,9 +215,8 @@
 
     @Test
     public void testHasUserModifiedDefaultPosition_true() {
-        new WindowManagerConfig().setLargeScreen().setLayoutDirection(
-                LAYOUT_DIRECTION_RTL).setUpConfig();
-        mPositioner.update();
+        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
+        mPositioner.update(deviceConfig);
 
         assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
 
@@ -266,12 +230,12 @@
         Insets insets = Insets.of(10, 20, 5, 15);
         Rect screenBounds = new Rect(0, 0, 1800, 2600);
 
-        new WindowManagerConfig()
+        DeviceConfig deviceConfig = new ConfigBuilder()
                 .setLargeScreen()
                 .setInsets(insets)
                 .setScreenBounds(screenBounds)
-                .setUpConfig();
-        mPositioner.update();
+                .build();
+        mPositioner.update(deviceConfig);
 
         Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName());
         Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor());
@@ -311,58 +275,47 @@
      * Sets up window manager to return config values based on what you need for the test.
      * By default it sets up a portrait phone without any insets.
      */
-    private class WindowManagerConfig {
+    private static class ConfigBuilder {
         private Rect mScreenBounds = new Rect(0, 0, 1000, 2000);
         private boolean mIsLargeScreen = false;
-        private int mOrientation = ORIENTATION_PORTRAIT;
-        private int mLayoutDirection = LAYOUT_DIRECTION_LTR;
+        private boolean mIsSmallTablet = false;
+        private boolean mIsLandscape = false;
+        private boolean mIsRtl = false;
         private Insets mInsets = Insets.of(0, 0, 0, 0);
 
-        public WindowManagerConfig setScreenBounds(Rect screenBounds) {
+        public ConfigBuilder setScreenBounds(Rect screenBounds) {
             mScreenBounds = screenBounds;
             return this;
         }
 
-        public WindowManagerConfig setLargeScreen() {
+        public ConfigBuilder setLargeScreen() {
             mIsLargeScreen = true;
             return this;
         }
 
-        public WindowManagerConfig setOrientation(int orientation) {
-            mOrientation = orientation;
+        public ConfigBuilder setSmallTablet() {
+            mIsSmallTablet = true;
             return this;
         }
 
-        public WindowManagerConfig setLayoutDirection(int layoutDirection) {
-            mLayoutDirection = layoutDirection;
+        public ConfigBuilder setLandscape() {
+            mIsLandscape = true;
             return this;
         }
 
-        public WindowManagerConfig setInsets(Insets insets) {
+        public ConfigBuilder setRtl() {
+            mIsRtl = true;
+            return this;
+        }
+
+        public ConfigBuilder setInsets(Insets insets) {
             mInsets = insets;
             return this;
         }
 
-        public void setUpConfig() {
-            mConfiguration.smallestScreenWidthDp = mIsLargeScreen
-                    ? MIN_WIDTH_FOR_TABLET
-                    : MIN_WIDTH_FOR_TABLET - 1;
-            mConfiguration.orientation = mOrientation;
-            mConfiguration.screenWidthDp = pxToDp(mScreenBounds.width());
-            mConfiguration.screenHeightDp = pxToDp(mScreenBounds.height());
-
-            when(mConfiguration.getLayoutDirection()).thenReturn(mLayoutDirection);
-            WindowInsets windowInsets = mock(WindowInsets.class);
-            when(windowInsets.getInsetsIgnoringVisibility(anyInt())).thenReturn(mInsets);
-            when(mWindowMetrics.getWindowInsets()).thenReturn(windowInsets);
-            when(mWindowMetrics.getBounds()).thenReturn(mScreenBounds);
-            when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
-        }
-
-        private int pxToDp(float px) {
-            int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
-            float dp = px / ((float) dpi / DisplayMetrics.DENSITY_DEFAULT);
-            return (int) dp;
+        private DeviceConfig build() {
+            return new DeviceConfig(mIsLargeScreen, mIsSmallTablet, mIsLandscape, mIsRtl,
+                    mScreenBounds, mInsets);
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
index 44ff354..c4b9c9b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
@@ -55,8 +55,6 @@
 
     private BubblesNavBarMotionEventHandler mMotionEventHandler;
     @Mock
-    private WindowManager mWindowManager;
-    @Mock
     private Runnable mInterceptTouchRunnable;
     @Mock
     private MotionEventListener mMotionEventListener;
@@ -66,7 +64,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         TestableBubblePositioner positioner = new TestableBubblePositioner(getContext(),
-                mWindowManager);
+                getContext().getSystemService(WindowManager.class));
         mMotionEventHandler = new BubblesNavBarMotionEventHandler(getContext(), positioner,
                 mInterceptTouchRunnable, mMotionEventListener);
         mMotionEventTime = SystemClock.uptimeMillis();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
index 335222e..6403e79 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -66,7 +66,8 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mPositioner = new BubblePositioner(getContext(), mock(WindowManager.class));
+        mPositioner = new BubblePositioner(getContext(),
+                getContext().getSystemService(WindowManager.class));
         mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
                 Insets.of(0, 0, 0, 0),
                 new Rect(0, 0, mDisplayWidth, mDisplayHeight));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java
index 991913a..f660987 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java
@@ -50,9 +50,6 @@
     private ExpandedViewAnimationController mController;
 
     @Mock
-    private WindowManager mWindowManager;
-
-    @Mock
     private BubbleExpandedView mMockExpandedView;
 
     @Before
@@ -60,7 +57,7 @@
         MockitoAnnotations.initMocks(this);
 
         TestableBubblePositioner positioner = new TestableBubblePositioner(getContext(),
-                mWindowManager);
+                getContext().getSystemService(WindowManager.class));
         mController = new ExpandedViewAnimationControllerImpl(getContext(), positioner);
 
         mController.setExpandedView(mMockExpandedView);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
index 31fafca..0c22908 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
@@ -313,7 +313,8 @@
                     bubbleCountSupplier,
                     onBubbleAnimatedOutAction,
                     onStackAnimationFinished,
-                    new TestableBubblePositioner(mContext, mock(WindowManager.class)));
+                    new TestableBubblePositioner(mContext,
+                            mContext.getSystemService(WindowManager.class)));
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index fde6acb..94c862b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -63,6 +63,7 @@
 import com.android.wm.shell.transition.OneShotRemoteHandler
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
+import com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_DESKTOP_MODE
 import com.android.wm.shell.transition.Transitions.TransitionHandler
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
 import com.google.common.truth.Truth.assertThat
@@ -392,8 +393,8 @@
     fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() {
         val task = setUpFreeformTask()
         task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
-        controller.moveToFullscreen(task)
-        val wct = getLatestWct(type = TRANSIT_CHANGE)
+        controller.moveToFullscreen(task.taskId, desktopModeWindowDecoration)
+        val wct = getLatestExitDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
             .isEqualTo(WINDOWING_MODE_UNDEFINED)
     }
@@ -402,15 +403,15 @@
     fun moveToFullscreen_displayFreeform_windowingModeSetToFullscreen() {
         val task = setUpFreeformTask()
         task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
-        controller.moveToFullscreen(task)
-        val wct = getLatestWct(type = TRANSIT_CHANGE)
+        controller.moveToFullscreen(task.taskId, desktopModeWindowDecoration)
+        val wct = getLatestExitDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
                 .isEqualTo(WINDOWING_MODE_FULLSCREEN)
     }
 
     @Test
     fun moveToFullscreen_nonExistentTask_doesNothing() {
-        controller.moveToFullscreen(999)
+        controller.moveToFullscreen(999, desktopModeWindowDecoration)
         verifyWCTNotExecuted()
     }
 
@@ -419,9 +420,9 @@
         val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
         val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY)
 
-        controller.moveToFullscreen(taskDefaultDisplay)
+        controller.moveToFullscreen(taskDefaultDisplay.taskId, desktopModeWindowDecoration)
 
-        with(getLatestWct(type = TRANSIT_CHANGE)) {
+        with(getLatestExitDesktopWct()) {
             assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
             assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
         }
@@ -808,6 +809,17 @@
         return arg.value
     }
 
+    private fun getLatestExitDesktopWct(): WindowContainerTransaction {
+        val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        if (ENABLE_SHELL_TRANSITIONS) {
+            verify(exitDesktopTransitionHandler)
+                    .startTransition(eq(TRANSIT_EXIT_DESKTOP_MODE), arg.capture(), any(), any())
+        } else {
+            verify(shellTaskOrganizer).applyTransaction(arg.capture())
+        }
+        return arg.value
+    }
+
     private fun verifyWCTNotExecuted() {
         if (ENABLE_SHELL_TRANSITIONS) {
             verify(transitions, never()).startTransition(anyInt(), any(), isNull())
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9ae6f8d..1e32349 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -19,6 +19,7 @@
 import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
 import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.media.audio.Flags.autoPublicVolumeApiHardening;
 import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
 
 import android.Manifest;
@@ -49,6 +50,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.media.AudioAttributes.AttributeSystemUsage;
 import android.media.CallbackUtil.ListenerInfo;
 import android.media.audiopolicy.AudioPolicy;
@@ -901,6 +903,7 @@
     @UnsupportedAppUsage
     public AudioManager(Context context) {
         setContext(context);
+        initPlatform();
     }
 
     private Context getContext() {
@@ -914,6 +917,9 @@
     }
 
     private void setContext(Context context) {
+        if (context == null) {
+            return;
+        }
         mOriginalContextDeviceId = context.getDeviceId();
         mApplicationContext = context.getApplicationContext();
         if (mApplicationContext != null) {
@@ -1063,8 +1069,17 @@
      * @see #isVolumeFixed()
      */
     public void adjustVolume(int direction, @PublicVolumeFlags int flags) {
-        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
-        helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
+        if (applyAutoHardening()) {
+            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);
+        }
     }
 
     /**
@@ -1093,8 +1108,17 @@
      */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType,
             @PublicVolumeFlags int flags) {
-        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
-        helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
+        if (applyAutoHardening()) {
+            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);
+        }
     }
 
     /** @hide */
@@ -9969,6 +9993,30 @@
         }
     }
 
+    //====================================================================
+    // Flag related utilities
+
+    private boolean mIsAutomotive = false;
+
+    private void initPlatform() {
+        try {
+            final Context context = getContext();
+            if (context != null) {
+                mIsAutomotive = context.getPackageManager()
+                        .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Error querying system feature for AUTOMOTIVE", e);
+        }
+    }
+
+    private boolean applyAutoHardening() {
+        if (mIsAutomotive && autoPublicVolumeApiHardening()) {
+            return true;
+        }
+        return false;
+    }
+
     //---------------------------------------------------------
     // Inner classes
     //--------------------
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 42400d1..d14775f 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -500,6 +500,10 @@
             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/media/jni/Android.bp b/media/jni/Android.bp
index 6031ef7..94fce79 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -122,11 +122,6 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
-
-    // Workaround Clang LTO crash.
-    lto: {
-        never: true,
-    },
 }
 
 cc_library_shared {
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 8b5b726..cf5059c 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -44,9 +44,4 @@
         "-Wunreachable-code",
         "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
-
-    // Workaround Clang LTO crash.
-    lto: {
-        never: true,
-    },
 }
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index fd785a4..b795560 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -55,6 +55,8 @@
 ?application/vnd.android.haptics.vibration+xml ahv
 ?application/vnd.android.ota ota
 ?application/vnd.apple.mpegurl m3u8
+?application/vnd.apple.pkpass pkpass
+?application/vnd.apple.pkpasses pkpasses
 ?application/vnd.ms-pki.stl stl
 ?application/vnd.ms-powerpoint pot
 ?application/vnd.ms-wpl wpl
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 8d8254a..daedb1a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -298,14 +298,7 @@
                         broadcastIntent,
                         PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
 
-                try {
-                    session.commit(pendingIntent.getIntentSender());
-                } catch (Exception e) {
-                    Log.e(LOG_TAG, "Cannot install package: ", e);
-                    launchFailure(PackageInstaller.STATUS_FAILURE,
-                        PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
-                    return;
-                }
+                session.commit(pendingIntent.getIntentSender());
                 mCancelButton.setEnabled(false);
                 setFinishOnTouchOutside(false);
             } else {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
index 223e99e..5679694 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
@@ -72,7 +72,8 @@
 
 private fun UserManager.showInSettings(userInfo: UserInfo): Int {
     val userProperties = getUserProperties(userInfo.userHandle)
-    return if (userInfo.isQuietModeEnabled && userProperties.hideInSettingsInQuietMode) {
+    return if (userInfo.isQuietModeEnabled && userProperties.showInQuietMode
+            == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
         UserProperties.SHOW_IN_SETTINGS_NO
     } else {
         userProperties.showInSettings
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 1a5acf6..5aa2bfc 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1373,11 +1373,11 @@
     <string name="tv_media_transfer_earc_subtitle">Connected via eARC</string>
 
     <!-- TV media output switcher. Title for the default audio output of the device [CHAR LIMIT=NONE] -->
-    <string name="tv_media_transfer_default">TV Default</string>
+    <string name="tv_media_transfer_default">TV default</string>
     <!-- TV media output switcher. Subtitle for default audio output which is HDMI, e.g. TV dongle [CHAR LIMIT=NONE] -->
-    <string name="tv_media_transfer_hdmi">HDMI Output</string>
+    <string name="tv_media_transfer_hdmi">HDMI output</string>
     <!-- TV media output switcher. Subtitle for default audio output which is internal speaker, i.e. panel VTs [CHAR LIMIT=NONE] -->
-    <string name="tv_media_transfer_internal_speakers">Internal Speakers</string>
+    <string name="tv_media_transfer_internal_speakers">Internal speakers</string>
 
     <!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
     <string name="profile_connect_timeout_subtext">Problem connecting. Turn device off &amp; back on</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
index 83c106b..92db508 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.provider.Settings;
+import android.os.UserManager;
 import android.util.ArraySet;
 import android.view.accessibility.AccessibilityManager;
 
@@ -68,4 +69,10 @@
         }
         return packageNames;
     }
+
+    /** Returns true if current user is a work profile user. */
+    public static boolean isWorkProfile(Context context) {
+        final UserManager userManager = context.getSystemService(UserManager.class);
+        return userManager.isManagedProfile() && !userManager.isSystemUser();
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 5dacba5..52b51d7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -413,12 +413,8 @@
      */
     @NonNull
     List<MediaDevice> getSelectedMediaDevices() {
-        if (TextUtils.isEmpty(mPackageName)) {
-            Log.w(TAG, "getSelectedMediaDevices() package name is null or empty!");
-            return Collections.emptyList();
-        }
+        RoutingSessionInfo info = getRoutingSessionInfo();
 
-        final RoutingSessionInfo info = getRoutingSessionInfo();
         if (info == null) {
             Log.w(TAG, "getSelectedMediaDevices() cannot find selectable MediaDevice from : "
                     + mPackageName);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
index 3514932..02ec90d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
@@ -48,6 +48,17 @@
             "com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG";
 
     /**
+     * An intent action to launch a media output dialog without any app or playback metadata, which
+     * only controls system routing.
+     *
+     * <p>System routes are those provided by the system, such as built-in speakers, wired headsets,
+     * bluetooth devices, and other outputs that require the app to feed media samples to the
+     * framework.
+     */
+    public static final String ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG =
+            "com.android.systemui.action.LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG";
+
+    /**
      * An intent action to launch media output broadcast dialog.
      */
     public static final String ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG =
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java
index c3e0c0b..6424352 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.view.accessibility.AccessibilityManager;
 
@@ -40,6 +41,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
 import org.robolectric.shadows.ShadowAccessibilityManager;
 
 import java.util.Arrays;
@@ -99,6 +101,20 @@
                 .containsExactly(DEFAULT_TTS_PACKAGE, ACCESSIBILITY_PACKAGE);
     }
 
+    @Test
+    public void isWorkProfile_defaultValue_returnFalse() {
+        assertThat(BatteryUtils.isWorkProfile(mContext)).isFalse();
+    }
+
+    @Test
+    public void isWorkProfile_workProfileMode_returnTrue() {
+        final UserManager userManager = mContext.getSystemService(UserManager.class);
+        Shadows.shadowOf(userManager).setManagedProfile(true);
+        Shadows.shadowOf(userManager).setIsSystemUser(false);
+
+        assertThat(BatteryUtils.isWorkProfile(mContext)).isTrue();
+    }
+
     private void setTtsPackageName(String defaultTtsPackageName) {
         Settings.Secure.putString(mContext.getContentResolver(),
                 Settings.Secure.TTS_DEFAULT_SYNTH, defaultTtsPackageName);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e218308..0a71cda 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1044,6 +1044,7 @@
                   android:exported="true">
             <intent-filter android:priority="1">
                 <action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG" />
+                <action android:name="com.android.systemui.action.LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG" />
                 <action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG" />
                 <action android:name="com.android.systemui.action.DISMISS_MEDIA_OUTPUT_DIALOG" />
             </intent-filter>
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a745ab5..a9dc145 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -143,8 +143,17 @@
 }
 
 flag {
+   name: "theme_overlay_controller_wakefulness_deprecation"
+   namespace: "systemui"
+   description: "Replacing WakefulnessLifecycle by KeyguardTransitionInteractor in "
+        "ThemOverlayController to mitigate flickering when locking the device"
+   bug: "308676488"
+}
+
+flag {
    name: "media_in_scene_container"
    namespace: "systemui"
    description: "Enable media in the scene container framework"
    bug: "296122467"
 }
+
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
index ddc3d3a..1860c9f 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
@@ -18,8 +18,8 @@
 
 import android.app.AlertDialog
 import android.content.Context
+import com.android.systemui.bouncer.ui.composable.BouncerDialogFactory
 import com.android.systemui.bouncer.ui.composable.BouncerScene
-import com.android.systemui.bouncer.ui.composable.BouncerSceneDialogFactory
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.scene.shared.model.Scene
@@ -38,8 +38,8 @@
 
         @Provides
         @SysUISingleton
-        fun bouncerSceneDialogFactory(@Application context: Context): BouncerSceneDialogFactory {
-            return object : BouncerSceneDialogFactory {
+        fun bouncerSceneDialogFactory(@Application context: Context): BouncerDialogFactory {
+            return object : BouncerDialogFactory {
                 override fun invoke(): AlertDialog {
                     return SystemUIDialog(context)
                 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
new file mode 100644
index 0000000..ba80a8d
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -0,0 +1,734 @@
+/*
+ * 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.bouncer.ui.composable
+
+import android.app.AlertDialog
+import android.app.Dialog
+import android.content.DialogInterface
+import androidx.compose.animation.Crossfade
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.snap
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.KeyboardArrowDown
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.DpOffset
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.times
+import com.android.compose.PlatformButton
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.compose.animation.scene.transitions
+import com.android.compose.modifiers.thenIf
+import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
+import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
+import com.android.systemui.common.shared.model.Text.Companion.loadText
+import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.fold.ui.composable.foldPosture
+import com.android.systemui.fold.ui.helper.FoldPosture
+import com.android.systemui.res.R
+import kotlin.math.abs
+import kotlin.math.max
+import kotlin.math.pow
+
+@Composable
+fun BouncerContent(
+    viewModel: BouncerViewModel,
+    dialogFactory: BouncerDialogFactory,
+    modifier: Modifier
+) {
+    val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible
+    val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState()
+    val layout = calculateLayout(isSideBySideSupported = isSideBySideSupported)
+
+    when (layout) {
+        BouncerSceneLayout.STANDARD ->
+            StandardLayout(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                modifier = modifier,
+            )
+        BouncerSceneLayout.SIDE_BY_SIDE ->
+            SideBySideLayout(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
+                modifier = modifier,
+            )
+        BouncerSceneLayout.STACKED ->
+            StackedLayout(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
+                modifier = modifier,
+            )
+        BouncerSceneLayout.SPLIT ->
+            SplitLayout(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                modifier = modifier,
+            )
+    }
+}
+
+/**
+ * Renders the contents of the actual bouncer UI, the area that takes user input to do an
+ * authentication attempt, including all messaging UI (directives, reasoning, errors, etc.).
+ */
+@Composable
+private fun StandardLayout(
+    viewModel: BouncerViewModel,
+    dialogFactory: BouncerDialogFactory,
+    modifier: Modifier = Modifier,
+    outputOnly: Boolean = false,
+) {
+    val foldPosture: FoldPosture by foldPosture()
+    val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState()
+    val isSplitAroundTheFold =
+        foldPosture == FoldPosture.Tabletop && !outputOnly && isSplitAroundTheFoldRequired
+    val currentSceneKey =
+        if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
+
+    SceneTransitionLayout(
+        currentScene = currentSceneKey,
+        onChangeScene = {},
+        transitions = SceneTransitions,
+        modifier = modifier,
+    ) {
+        scene(SceneKeys.ContiguousSceneKey) {
+            FoldSplittable(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                outputOnly = outputOnly,
+                isSplit = false,
+            )
+        }
+
+        scene(SceneKeys.SplitSceneKey) {
+            FoldSplittable(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                outputOnly = outputOnly,
+                isSplit = true,
+            )
+        }
+    }
+}
+
+/**
+ * Renders the "standard" layout of the bouncer, where the bouncer is rendered on its own (no user
+ * switcher UI) and laid out vertically, centered horizontally.
+ *
+ * If [isSplit] is `true`, the top and bottom parts of the bouncer are split such that they don't
+ * render across the location of the fold hardware when the device is fully or part-way unfolded
+ * with the fold hinge in a horizontal position.
+ *
+ * If [outputOnly] is `true`, only the "output" part of the UI is shown (where the entered PIN
+ * "shapes" appear), if `false`, the entire UI is shown, including the area where the user can enter
+ * their PIN or pattern.
+ */
+@Composable
+private fun SceneScope.FoldSplittable(
+    viewModel: BouncerViewModel,
+    dialogFactory: BouncerDialogFactory,
+    outputOnly: Boolean,
+    isSplit: Boolean,
+    modifier: Modifier = Modifier,
+) {
+    val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
+    val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
+    var dialog: Dialog? by remember { mutableStateOf(null) }
+    val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
+    val splitRatio =
+        LocalContext.current.resources.getFloat(
+            R.dimen.motion_layout_half_fold_bouncer_height_ratio
+        )
+
+    Column(modifier = modifier.padding(horizontal = 32.dp)) {
+        // Content above the fold, when split on a foldable device in a "table top" posture:
+        Box(
+            modifier =
+                Modifier.element(SceneElements.AboveFold).fillMaxWidth().thenIf(isSplit) {
+                    Modifier.weight(splitRatio)
+                },
+        ) {
+            Column(
+                horizontalAlignment = Alignment.CenterHorizontally,
+                modifier = Modifier.fillMaxWidth().padding(top = 92.dp),
+            ) {
+                Crossfade(
+                    targetState = message,
+                    label = "Bouncer message",
+                    animationSpec = if (message.isUpdateAnimated) tween() else snap(),
+                ) { message ->
+                    Text(
+                        text = message.text,
+                        color = MaterialTheme.colorScheme.onSurface,
+                        style = MaterialTheme.typography.bodyLarge,
+                    )
+                }
+
+                Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
+
+                UserInputArea(
+                    viewModel = viewModel,
+                    visibility = UserInputAreaVisibility.OUTPUT_ONLY,
+                )
+            }
+        }
+
+        // Content below the fold, when split on a foldable device in a "table top" posture:
+        Box(
+            modifier =
+                Modifier.element(SceneElements.BelowFold).fillMaxWidth().thenIf(isSplit) {
+                    Modifier.weight(1 - splitRatio)
+                },
+        ) {
+            Column(
+                horizontalAlignment = Alignment.CenterHorizontally,
+                modifier = Modifier.fillMaxWidth(),
+            ) {
+                if (!outputOnly) {
+                    Box(Modifier.weight(1f)) {
+                        UserInputArea(
+                            viewModel = viewModel,
+                            visibility = UserInputAreaVisibility.INPUT_ONLY,
+                            modifier = Modifier.align(Alignment.Center),
+                        )
+                    }
+                }
+
+                Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
+
+                val actionButtonModifier = Modifier.height(56.dp)
+
+                actionButton.let { actionButtonViewModel ->
+                    if (actionButtonViewModel != null) {
+                        BouncerActionButton(
+                            viewModel = actionButtonViewModel,
+                            modifier = actionButtonModifier,
+                        )
+                    } else {
+                        Spacer(modifier = actionButtonModifier)
+                    }
+                }
+
+                Spacer(Modifier.height(48.dp))
+            }
+        }
+
+        if (dialogMessage != null) {
+            if (dialog == null) {
+                dialog =
+                    dialogFactory().apply {
+                        setMessage(dialogMessage)
+                        setButton(
+                            DialogInterface.BUTTON_NEUTRAL,
+                            context.getString(R.string.ok),
+                        ) { _, _ ->
+                            viewModel.onThrottlingDialogDismissed()
+                        }
+                        setCancelable(false)
+                        setCanceledOnTouchOutside(false)
+                        show()
+                    }
+            }
+        } else {
+            dialog?.dismiss()
+            dialog = null
+        }
+    }
+}
+
+/**
+ * Renders the user input area, where the user interacts with the UI to enter their credentials.
+ *
+ * For example, this can be the pattern input area, the password text box, or pin pad.
+ */
+@Composable
+private fun UserInputArea(
+    viewModel: BouncerViewModel,
+    visibility: UserInputAreaVisibility,
+    modifier: Modifier = Modifier,
+) {
+    val authMethodViewModel: AuthMethodBouncerViewModel? by
+        viewModel.authMethodViewModel.collectAsState()
+
+    when (val nonNullViewModel = authMethodViewModel) {
+        is PinBouncerViewModel ->
+            when (visibility) {
+                UserInputAreaVisibility.OUTPUT_ONLY ->
+                    PinInputDisplay(
+                        viewModel = nonNullViewModel,
+                        modifier = modifier,
+                    )
+                UserInputAreaVisibility.INPUT_ONLY ->
+                    PinPad(
+                        viewModel = nonNullViewModel,
+                        modifier = modifier,
+                    )
+            }
+        is PasswordBouncerViewModel ->
+            if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
+                PasswordBouncer(
+                    viewModel = nonNullViewModel,
+                    modifier = modifier,
+                )
+            }
+        is PatternBouncerViewModel ->
+            if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
+                PatternBouncer(
+                    viewModel = nonNullViewModel,
+                    modifier = modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
+                )
+            }
+        else -> Unit
+    }
+}
+
+/**
+ * Renders the action button on the bouncer, which triggers either Return to Call or Emergency Call.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun BouncerActionButton(
+    viewModel: BouncerActionButtonModel,
+    modifier: Modifier = Modifier,
+) {
+    Button(
+        onClick = viewModel.onClick,
+        modifier =
+            modifier.thenIf(viewModel.onLongClick != null) {
+                Modifier.combinedClickable(
+                    onClick = viewModel.onClick,
+                    onLongClick = viewModel.onLongClick,
+                )
+            },
+        colors =
+            ButtonDefaults.buttonColors(
+                containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+                contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
+            ),
+    ) {
+        Text(
+            text = viewModel.label,
+            style = MaterialTheme.typography.bodyMedium,
+        )
+    }
+}
+
+/** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
+@Composable
+private fun UserSwitcher(
+    viewModel: BouncerViewModel,
+    modifier: Modifier = Modifier,
+) {
+    val selectedUserImage by viewModel.selectedUserImage.collectAsState(null)
+    val dropdownItems by viewModel.userSwitcherDropdown.collectAsState(emptyList())
+
+    Column(
+        horizontalAlignment = Alignment.CenterHorizontally,
+        verticalArrangement = Arrangement.Center,
+        modifier = modifier,
+    ) {
+        selectedUserImage?.let {
+            Image(
+                bitmap = it.asImageBitmap(),
+                contentDescription = null,
+                modifier = Modifier.size(SelectedUserImageSize),
+            )
+        }
+
+        val (isDropdownExpanded, setDropdownExpanded) = remember { mutableStateOf(false) }
+
+        dropdownItems.firstOrNull()?.let { firstDropdownItem ->
+            Spacer(modifier = Modifier.height(40.dp))
+
+            Box {
+                PlatformButton(
+                    modifier =
+                        Modifier
+                            // Remove the built-in padding applied inside PlatformButton:
+                            .padding(vertical = 0.dp)
+                            .width(UserSwitcherDropdownWidth)
+                            .height(UserSwitcherDropdownHeight),
+                    colors =
+                        ButtonDefaults.buttonColors(
+                            containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+                            contentColor = MaterialTheme.colorScheme.onSurface,
+                        ),
+                    onClick = { setDropdownExpanded(!isDropdownExpanded) },
+                ) {
+                    val context = LocalContext.current
+                    Text(
+                        text = checkNotNull(firstDropdownItem.text.loadText(context)),
+                        style = MaterialTheme.typography.headlineSmall,
+                        maxLines = 1,
+                        overflow = TextOverflow.Ellipsis,
+                    )
+
+                    Spacer(modifier = Modifier.weight(1f))
+
+                    Icon(
+                        imageVector = Icons.Default.KeyboardArrowDown,
+                        contentDescription = null,
+                        modifier = Modifier.size(32.dp),
+                    )
+                }
+
+                UserSwitcherDropdownMenu(
+                    isExpanded = isDropdownExpanded,
+                    items = dropdownItems,
+                    onDismissed = { setDropdownExpanded(false) },
+                )
+            }
+        }
+    }
+}
+
+/**
+ * Renders the dropdown menu that displays the actual users and/or user actions that can be
+ * selected.
+ */
+@Composable
+private fun UserSwitcherDropdownMenu(
+    isExpanded: Boolean,
+    items: List<BouncerViewModel.UserSwitcherDropdownItemViewModel>,
+    onDismissed: () -> Unit,
+) {
+    val context = LocalContext.current
+
+    // TODO(b/303071855): once the FR is fixed, remove this composition local override.
+    MaterialTheme(
+        colorScheme =
+            MaterialTheme.colorScheme.copy(
+                surface = MaterialTheme.colorScheme.surfaceContainerHighest,
+            ),
+        shapes = MaterialTheme.shapes.copy(extraSmall = RoundedCornerShape(28.dp)),
+    ) {
+        DropdownMenu(
+            expanded = isExpanded,
+            onDismissRequest = onDismissed,
+            offset =
+                DpOffset(
+                    x = 0.dp,
+                    y = -UserSwitcherDropdownHeight,
+                ),
+            modifier = Modifier.width(UserSwitcherDropdownWidth),
+        ) {
+            items.forEach { userSwitcherDropdownItem ->
+                DropdownMenuItem(
+                    leadingIcon = {
+                        Icon(
+                            icon = userSwitcherDropdownItem.icon,
+                            tint = MaterialTheme.colorScheme.primary,
+                            modifier = Modifier.size(28.dp),
+                        )
+                    },
+                    text = {
+                        Text(
+                            text = checkNotNull(userSwitcherDropdownItem.text.loadText(context)),
+                            style = MaterialTheme.typography.bodyLarge,
+                            color = MaterialTheme.colorScheme.onSurface,
+                        )
+                    },
+                    onClick = {
+                        onDismissed()
+                        userSwitcherDropdownItem.onClick()
+                    },
+                )
+            }
+        }
+    }
+}
+
+/**
+ * Renders the bouncer UI in split mode, with half on one side and half on the other side, swappable
+ * by double-tapping on the side.
+ */
+@Composable
+private fun SplitLayout(
+    viewModel: BouncerViewModel,
+    dialogFactory: BouncerDialogFactory,
+    modifier: Modifier = Modifier,
+) {
+    SwappableLayout(
+        startContent = { startContentModifier ->
+            StandardLayout(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                outputOnly = true,
+                modifier = startContentModifier,
+            )
+        },
+        endContent = { endContentModifier ->
+            UserInputArea(
+                viewModel = viewModel,
+                visibility = UserInputAreaVisibility.INPUT_ONLY,
+                modifier = endContentModifier,
+            )
+        },
+        modifier = modifier
+    )
+}
+
+/**
+ * Arranges the given two contents side-by-side, supporting a double tap anywhere on the background
+ * to flip their positions.
+ */
+@Composable
+private fun SwappableLayout(
+    startContent: @Composable (Modifier) -> Unit,
+    endContent: @Composable (Modifier) -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    val layoutDirection = LocalLayoutDirection.current
+    val isLeftToRight = layoutDirection == LayoutDirection.Ltr
+    val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) }
+
+    Row(
+        modifier =
+            modifier.pointerInput(Unit) {
+                detectTapGestures(
+                    onDoubleTap = { offset ->
+                        // Depending on where the user double tapped, switch the elements such that
+                        // the endContent is closer to the side that was double tapped.
+                        setSwapped(offset.x < size.width / 2)
+                    }
+                )
+            },
+    ) {
+        val animatedOffset by
+            animateFloatAsState(
+                targetValue =
+                    if (!isSwapped) {
+                        // When startContent is first, both elements have their natural placement so
+                        // they are not offset in any way.
+                        0f
+                    } else if (isLeftToRight) {
+                        // Since startContent is not first, the elements have to be swapped
+                        // horizontally. In the case of LTR locales, this means pushing startContent
+                        // to the right, hence the positive number.
+                        1f
+                    } else {
+                        // Since startContent is not first, the elements have to be swapped
+                        // horizontally. In the case of RTL locales, this means pushing startContent
+                        // to the left, hence the negative number.
+                        -1f
+                    },
+                label = "offset",
+            )
+
+        startContent(
+            Modifier.fillMaxHeight().weight(1f).graphicsLayer {
+                translationX = size.width * animatedOffset
+                alpha = animatedAlpha(animatedOffset)
+            }
+        )
+
+        Box(
+            modifier =
+                Modifier.fillMaxHeight().weight(1f).graphicsLayer {
+                    // A negative sign is used to make sure this is offset in the direction that's
+                    // opposite of the direction that the user switcher is pushed in.
+                    translationX = -size.width * animatedOffset
+                    alpha = animatedAlpha(animatedOffset)
+                }
+        ) {
+            endContent(Modifier.widthIn(max = 400.dp).align(Alignment.BottomCenter))
+        }
+    }
+}
+
+/**
+ * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap
+ * anywhere on the background to flip their positions.
+ *
+ * In situations when [isUserSwitcherVisible] is `false`, one of two things may happen: either the
+ * UI for the bouncer will be shown on its own, taking up one side, with the other side just being
+ * empty space or, if that kind of "stand-alone side-by-side" isn't supported, the standard
+ * rendering of the bouncer will be used instead of the side-by-side layout.
+ */
+@Composable
+private fun SideBySideLayout(
+    viewModel: BouncerViewModel,
+    dialogFactory: BouncerDialogFactory,
+    isUserSwitcherVisible: Boolean,
+    modifier: Modifier = Modifier,
+) {
+    SwappableLayout(
+        startContent = { startContentModifier ->
+            if (isUserSwitcherVisible) {
+                UserSwitcher(
+                    viewModel = viewModel,
+                    modifier = startContentModifier,
+                )
+            } else {
+                Box(
+                    modifier = startContentModifier,
+                )
+            }
+        },
+        endContent = { endContentModifier ->
+            StandardLayout(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                modifier = endContentModifier,
+            )
+        },
+        modifier = modifier,
+    )
+}
+
+/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */
+@Composable
+private fun StackedLayout(
+    viewModel: BouncerViewModel,
+    dialogFactory: BouncerDialogFactory,
+    isUserSwitcherVisible: Boolean,
+    modifier: Modifier = Modifier,
+) {
+    Column(
+        modifier = modifier,
+    ) {
+        if (isUserSwitcherVisible) {
+            UserSwitcher(
+                viewModel = viewModel,
+                modifier = Modifier.fillMaxWidth().weight(1f),
+            )
+        }
+
+        StandardLayout(
+            viewModel = viewModel,
+            dialogFactory = dialogFactory,
+            modifier = Modifier.fillMaxWidth().weight(1f),
+        )
+    }
+}
+
+interface BouncerDialogFactory {
+    operator fun invoke(): AlertDialog
+}
+
+/** Enumerates all supported user-input area visibilities. */
+private enum class UserInputAreaVisibility {
+    /**
+     * Only the area where the user enters the input is shown; the area where the input is reflected
+     * back to the user is not shown.
+     */
+    INPUT_ONLY,
+    /**
+     * Only the area where the input is reflected back to the user is shown; the area where the
+     * input is entered by the user is not shown.
+     */
+    OUTPUT_ONLY,
+}
+
+/**
+ * Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of
+ * the two reaches a stopping point but `0` in the middle of the transition.
+ */
+private fun animatedAlpha(
+    offset: Float,
+): Float {
+    // Describes a curve that is made of two parabolic U-shaped curves mirrored horizontally around
+    // the y-axis. The U on the left runs between x = -1 and x = 0 while the U on the right runs
+    // between x = 0 and x = 1.
+    //
+    // The minimum values of the curves are at -0.5 and +0.5.
+    //
+    // Both U curves are vertically scaled such that they reach the points (-1, 1) and (1, 1).
+    //
+    // Breaking it down, it's y = a×(|x|-m)²+b, where:
+    // x: the offset
+    // y: the alpha
+    // m: x-axis center of the parabolic curves, where the minima are.
+    // b: y-axis offset to apply to the entire curve so the animation spends more time with alpha =
+    // 0.
+    // a: amplitude to scale the parabolic curves to reach y = 1 at x = -1, x = 0, and x = +1.
+    val m = 0.5f
+    val b = -0.25
+    val a = (1 - b) / m.pow(2)
+
+    return max(0f, (a * (abs(offset) - m).pow(2) + b).toFloat())
+}
+
+private val SelectedUserImageSize = 190.dp
+private val UserSwitcherDropdownWidth = SelectedUserImageSize + 2 * 29.dp
+private val UserSwitcherDropdownHeight = 60.dp
+
+private object SceneKeys {
+    val ContiguousSceneKey = SceneKey("default")
+    val SplitSceneKey = SceneKey("split")
+}
+
+private object SceneElements {
+    val AboveFold = ElementKey("above_fold")
+    val BelowFold = ElementKey("below_fold")
+}
+
+private val SceneTransitions = transitions {
+    from(SceneKeys.ContiguousSceneKey, to = SceneKeys.SplitSceneKey) { spec = tween() }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 57af2ba..d638ffe 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -16,91 +16,22 @@
 
 package com.android.systemui.bouncer.ui.composable
 
-import android.app.AlertDialog
-import android.app.Dialog
-import android.content.DialogInterface
-import androidx.compose.animation.Crossfade
-import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.snap
-import androidx.compose.animation.core.tween
 import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.gestures.detectTapGestures
-import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.aspectRatio
-import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.widthIn
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.KeyboardArrowDown
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.DropdownMenu
-import androidx.compose.material3.DropdownMenuItem
-import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.asImageBitmap
-import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.DpOffset
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.times
-import com.android.compose.PlatformButton
 import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.SceneKey as SceneTransitionLayoutSceneKey
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.SceneTransitionLayout
-import com.android.compose.animation.scene.transitions
-import com.android.compose.modifiers.thenIf
-import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
-import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
-import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
-import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
-import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
-import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
-import com.android.systemui.common.shared.model.Text.Companion.loadText
-import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.fold.ui.composable.foldPosture
-import com.android.systemui.fold.ui.helper.FoldPosture
-import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Direction
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.UserAction
 import com.android.systemui.scene.ui.composable.ComposableScene
 import javax.inject.Inject
-import kotlin.math.abs
-import kotlin.math.max
-import kotlin.math.pow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -118,7 +49,7 @@
 @Inject
 constructor(
     private val viewModel: BouncerViewModel,
-    private val dialogFactory: BouncerSceneDialogFactory,
+    private val dialogFactory: BouncerDialogFactory,
 ) : ComposableScene {
     override val key = SceneKey.Bouncer
 
@@ -140,648 +71,22 @@
 @Composable
 private fun SceneScope.BouncerScene(
     viewModel: BouncerViewModel,
-    dialogFactory: BouncerSceneDialogFactory,
+    dialogFactory: BouncerDialogFactory,
     modifier: Modifier = Modifier,
 ) {
     val backgroundColor = MaterialTheme.colorScheme.surface
-    val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState()
-    val layout = calculateLayout(isSideBySideSupported = isSideBySideSupported)
 
     Box(modifier) {
         Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
             drawRect(color = backgroundColor)
         }
 
-        val childModifier = Modifier.element(Bouncer.Elements.Content).fillMaxSize()
-        val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible
-
-        when (layout) {
-            BouncerSceneLayout.STANDARD ->
-                StandardLayout(
-                    viewModel = viewModel,
-                    dialogFactory = dialogFactory,
-                    modifier = childModifier,
-                )
-            BouncerSceneLayout.SIDE_BY_SIDE ->
-                SideBySideLayout(
-                    viewModel = viewModel,
-                    dialogFactory = dialogFactory,
-                    isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
-                    modifier = childModifier,
-                )
-            BouncerSceneLayout.STACKED ->
-                StackedLayout(
-                    viewModel = viewModel,
-                    dialogFactory = dialogFactory,
-                    isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
-                    modifier = childModifier,
-                )
-            BouncerSceneLayout.SPLIT ->
-                SplitLayout(
-                    viewModel = viewModel,
-                    dialogFactory = dialogFactory,
-                    modifier = childModifier,
-                )
-        }
-    }
-}
-
-/**
- * Renders the contents of the actual bouncer UI, the area that takes user input to do an
- * authentication attempt, including all messaging UI (directives, reasoning, errors, etc.).
- */
-@Composable
-private fun StandardLayout(
-    viewModel: BouncerViewModel,
-    dialogFactory: BouncerSceneDialogFactory,
-    modifier: Modifier = Modifier,
-    outputOnly: Boolean = false,
-) {
-    val foldPosture: FoldPosture by foldPosture()
-    val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState()
-    val isSplitAroundTheFold =
-        foldPosture == FoldPosture.Tabletop && !outputOnly && isSplitAroundTheFoldRequired
-    val currentSceneKey =
-        if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
-
-    SceneTransitionLayout(
-        currentScene = currentSceneKey,
-        onChangeScene = {},
-        transitions = SceneTransitions,
-        modifier = modifier,
-    ) {
-        scene(SceneKeys.ContiguousSceneKey) {
-            FoldSplittable(
-                viewModel = viewModel,
-                dialogFactory = dialogFactory,
-                outputOnly = outputOnly,
-                isSplit = false,
-            )
-        }
-
-        scene(SceneKeys.SplitSceneKey) {
-            FoldSplittable(
-                viewModel = viewModel,
-                dialogFactory = dialogFactory,
-                outputOnly = outputOnly,
-                isSplit = true,
-            )
-        }
-    }
-}
-
-/**
- * Renders the "standard" layout of the bouncer, where the bouncer is rendered on its own (no user
- * switcher UI) and laid out vertically, centered horizontally.
- *
- * If [isSplit] is `true`, the top and bottom parts of the bouncer are split such that they don't
- * render across the location of the fold hardware when the device is fully or part-way unfolded
- * with the fold hinge in a horizontal position.
- *
- * If [outputOnly] is `true`, only the "output" part of the UI is shown (where the entered PIN
- * "shapes" appear), if `false`, the entire UI is shown, including the area where the user can enter
- * their PIN or pattern.
- */
-@Composable
-private fun SceneScope.FoldSplittable(
-    viewModel: BouncerViewModel,
-    dialogFactory: BouncerSceneDialogFactory,
-    outputOnly: Boolean,
-    isSplit: Boolean,
-    modifier: Modifier = Modifier,
-) {
-    val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
-    val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
-    var dialog: Dialog? by remember { mutableStateOf(null) }
-    val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
-    val splitRatio =
-        LocalContext.current.resources.getFloat(
-            R.dimen.motion_layout_half_fold_bouncer_height_ratio
-        )
-
-    Column(modifier = modifier.padding(horizontal = 32.dp)) {
-        // Content above the fold, when split on a foldable device in a "table top" posture:
-        Box(
-            modifier =
-                Modifier.element(SceneElements.AboveFold).fillMaxWidth().thenIf(isSplit) {
-                    Modifier.weight(splitRatio)
-                },
-        ) {
-            Column(
-                horizontalAlignment = Alignment.CenterHorizontally,
-                modifier = Modifier.fillMaxWidth().padding(top = 92.dp),
-            ) {
-                Crossfade(
-                    targetState = message,
-                    label = "Bouncer message",
-                    animationSpec = if (message.isUpdateAnimated) tween() else snap(),
-                ) { message ->
-                    Text(
-                        text = message.text,
-                        color = MaterialTheme.colorScheme.onSurface,
-                        style = MaterialTheme.typography.bodyLarge,
-                    )
-                }
-
-                Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
-
-                UserInputArea(
-                    viewModel = viewModel,
-                    visibility = UserInputAreaVisibility.OUTPUT_ONLY,
-                )
-            }
-        }
-
-        // Content below the fold, when split on a foldable device in a "table top" posture:
-        Box(
-            modifier =
-                Modifier.element(SceneElements.BelowFold).fillMaxWidth().thenIf(isSplit) {
-                    Modifier.weight(1 - splitRatio)
-                },
-        ) {
-            Column(
-                horizontalAlignment = Alignment.CenterHorizontally,
-                modifier = Modifier.fillMaxWidth(),
-            ) {
-                if (!outputOnly) {
-                    Box(Modifier.weight(1f)) {
-                        UserInputArea(
-                            viewModel = viewModel,
-                            visibility = UserInputAreaVisibility.INPUT_ONLY,
-                            modifier = Modifier.align(Alignment.Center),
-                        )
-                    }
-                }
-
-                Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
-
-                val actionButtonModifier = Modifier.height(56.dp)
-
-                actionButton.let { actionButtonViewModel ->
-                    if (actionButtonViewModel != null) {
-                        BouncerActionButton(
-                            viewModel = actionButtonViewModel,
-                            modifier = actionButtonModifier,
-                        )
-                    } else {
-                        Spacer(modifier = actionButtonModifier)
-                    }
-                }
-
-                Spacer(Modifier.height(48.dp))
-            }
-        }
-
-        if (dialogMessage != null) {
-            if (dialog == null) {
-                dialog =
-                    dialogFactory().apply {
-                        setMessage(dialogMessage)
-                        setButton(
-                            DialogInterface.BUTTON_NEUTRAL,
-                            context.getString(R.string.ok),
-                        ) { _, _ ->
-                            viewModel.onThrottlingDialogDismissed()
-                        }
-                        setCancelable(false)
-                        setCanceledOnTouchOutside(false)
-                        show()
-                    }
-            }
-        } else {
-            dialog?.dismiss()
-            dialog = null
-        }
-    }
-}
-
-/**
- * Renders the user input area, where the user interacts with the UI to enter their credentials.
- *
- * For example, this can be the pattern input area, the password text box, or pin pad.
- */
-@Composable
-private fun UserInputArea(
-    viewModel: BouncerViewModel,
-    visibility: UserInputAreaVisibility,
-    modifier: Modifier = Modifier,
-) {
-    val authMethodViewModel: AuthMethodBouncerViewModel? by
-        viewModel.authMethodViewModel.collectAsState()
-
-    when (val nonNullViewModel = authMethodViewModel) {
-        is PinBouncerViewModel ->
-            when (visibility) {
-                UserInputAreaVisibility.OUTPUT_ONLY ->
-                    PinInputDisplay(
-                        viewModel = nonNullViewModel,
-                        modifier = modifier,
-                    )
-                UserInputAreaVisibility.INPUT_ONLY ->
-                    PinPad(
-                        viewModel = nonNullViewModel,
-                        modifier = modifier,
-                    )
-            }
-        is PasswordBouncerViewModel ->
-            if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
-                PasswordBouncer(
-                    viewModel = nonNullViewModel,
-                    modifier = modifier,
-                )
-            }
-        is PatternBouncerViewModel ->
-            if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
-                PatternBouncer(
-                    viewModel = nonNullViewModel,
-                    modifier = modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
-                )
-            }
-        else -> Unit
-    }
-}
-
-/**
- * Renders the action button on the bouncer, which triggers either Return to Call or Emergency Call.
- */
-@OptIn(ExperimentalFoundationApi::class)
-@Composable
-private fun BouncerActionButton(
-    viewModel: BouncerActionButtonModel,
-    modifier: Modifier = Modifier,
-) {
-    Button(
-        onClick = viewModel.onClick,
-        modifier =
-            modifier.thenIf(viewModel.onLongClick != null) {
-                Modifier.combinedClickable(
-                    onClick = viewModel.onClick,
-                    onLongClick = viewModel.onLongClick,
-                )
-            },
-        colors =
-            ButtonDefaults.buttonColors(
-                containerColor = MaterialTheme.colorScheme.tertiaryContainer,
-                contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
-            ),
-    ) {
-        Text(
-            text = viewModel.label,
-            style = MaterialTheme.typography.bodyMedium,
+        // Separate the bouncer content into a reusable composable that doesn't have any SceneScope
+        // dependencies
+        BouncerContent(
+            viewModel,
+            dialogFactory,
+            Modifier.element(Bouncer.Elements.Content).fillMaxSize()
         )
     }
 }
-
-/** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
-@Composable
-private fun UserSwitcher(
-    viewModel: BouncerViewModel,
-    modifier: Modifier = Modifier,
-) {
-    val selectedUserImage by viewModel.selectedUserImage.collectAsState(null)
-    val dropdownItems by viewModel.userSwitcherDropdown.collectAsState(emptyList())
-
-    Column(
-        horizontalAlignment = Alignment.CenterHorizontally,
-        verticalArrangement = Arrangement.Center,
-        modifier = modifier,
-    ) {
-        selectedUserImage?.let {
-            Image(
-                bitmap = it.asImageBitmap(),
-                contentDescription = null,
-                modifier = Modifier.size(SelectedUserImageSize),
-            )
-        }
-
-        val (isDropdownExpanded, setDropdownExpanded) = remember { mutableStateOf(false) }
-
-        dropdownItems.firstOrNull()?.let { firstDropdownItem ->
-            Spacer(modifier = Modifier.height(40.dp))
-
-            Box {
-                PlatformButton(
-                    modifier =
-                        Modifier
-                            // Remove the built-in padding applied inside PlatformButton:
-                            .padding(vertical = 0.dp)
-                            .width(UserSwitcherDropdownWidth)
-                            .height(UserSwitcherDropdownHeight),
-                    colors =
-                        ButtonDefaults.buttonColors(
-                            containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
-                            contentColor = MaterialTheme.colorScheme.onSurface,
-                        ),
-                    onClick = { setDropdownExpanded(!isDropdownExpanded) },
-                ) {
-                    val context = LocalContext.current
-                    Text(
-                        text = checkNotNull(firstDropdownItem.text.loadText(context)),
-                        style = MaterialTheme.typography.headlineSmall,
-                        maxLines = 1,
-                        overflow = TextOverflow.Ellipsis,
-                    )
-
-                    Spacer(modifier = Modifier.weight(1f))
-
-                    Icon(
-                        imageVector = Icons.Default.KeyboardArrowDown,
-                        contentDescription = null,
-                        modifier = Modifier.size(32.dp),
-                    )
-                }
-
-                UserSwitcherDropdownMenu(
-                    isExpanded = isDropdownExpanded,
-                    items = dropdownItems,
-                    onDismissed = { setDropdownExpanded(false) },
-                )
-            }
-        }
-    }
-}
-
-/**
- * Renders the dropdowm menu that displays the actual users and/or user actions that can be
- * selected.
- */
-@Composable
-private fun UserSwitcherDropdownMenu(
-    isExpanded: Boolean,
-    items: List<BouncerViewModel.UserSwitcherDropdownItemViewModel>,
-    onDismissed: () -> Unit,
-) {
-    val context = LocalContext.current
-
-    // TODO(b/303071855): once the FR is fixed, remove this composition local override.
-    MaterialTheme(
-        colorScheme =
-            MaterialTheme.colorScheme.copy(
-                surface = MaterialTheme.colorScheme.surfaceContainerHighest,
-            ),
-        shapes = MaterialTheme.shapes.copy(extraSmall = RoundedCornerShape(28.dp)),
-    ) {
-        DropdownMenu(
-            expanded = isExpanded,
-            onDismissRequest = onDismissed,
-            offset =
-                DpOffset(
-                    x = 0.dp,
-                    y = -UserSwitcherDropdownHeight,
-                ),
-            modifier = Modifier.width(UserSwitcherDropdownWidth),
-        ) {
-            items.forEach { userSwitcherDropdownItem ->
-                DropdownMenuItem(
-                    leadingIcon = {
-                        Icon(
-                            icon = userSwitcherDropdownItem.icon,
-                            tint = MaterialTheme.colorScheme.primary,
-                            modifier = Modifier.size(28.dp),
-                        )
-                    },
-                    text = {
-                        Text(
-                            text = checkNotNull(userSwitcherDropdownItem.text.loadText(context)),
-                            style = MaterialTheme.typography.bodyLarge,
-                            color = MaterialTheme.colorScheme.onSurface,
-                        )
-                    },
-                    onClick = {
-                        onDismissed()
-                        userSwitcherDropdownItem.onClick()
-                    },
-                )
-            }
-        }
-    }
-}
-
-/**
- * Renders the bouncer UI in split mode, with half on one side and half on the other side, swappable
- * by double-tapping on the side.
- */
-@Composable
-private fun SplitLayout(
-    viewModel: BouncerViewModel,
-    dialogFactory: BouncerSceneDialogFactory,
-    modifier: Modifier = Modifier,
-) {
-    SwappableLayout(
-        startContent = { startContentModifier ->
-            StandardLayout(
-                viewModel = viewModel,
-                dialogFactory = dialogFactory,
-                outputOnly = true,
-                modifier = startContentModifier,
-            )
-        },
-        endContent = { endContentModifier ->
-            UserInputArea(
-                viewModel = viewModel,
-                visibility = UserInputAreaVisibility.INPUT_ONLY,
-                modifier = endContentModifier,
-            )
-        },
-        modifier = modifier
-    )
-}
-
-/**
- * Arranges the given two contents side-by-side, supporting a double tap anywhere on the background
- * to flip their positions.
- */
-@Composable
-private fun SwappableLayout(
-    startContent: @Composable (Modifier) -> Unit,
-    endContent: @Composable (Modifier) -> Unit,
-    modifier: Modifier = Modifier,
-) {
-    val layoutDirection = LocalLayoutDirection.current
-    val isLeftToRight = layoutDirection == LayoutDirection.Ltr
-    val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) }
-
-    Row(
-        modifier =
-            modifier.pointerInput(Unit) {
-                detectTapGestures(
-                    onDoubleTap = { offset ->
-                        // Depending on where the user double tapped, switch the elements such that
-                        // the endContent is closer to the side that was double tapped.
-                        setSwapped(offset.x < size.width / 2)
-                    }
-                )
-            },
-    ) {
-        val animatedOffset by
-            animateFloatAsState(
-                targetValue =
-                    if (!isSwapped) {
-                        // When startContent is first, both elements have their natural placement so
-                        // they are not offset in any way.
-                        0f
-                    } else if (isLeftToRight) {
-                        // Since startContent is not first, the elements have to be swapped
-                        // horizontally. In the case of LTR locales, this means pushing startContent
-                        // to the right, hence the positive number.
-                        1f
-                    } else {
-                        // Since startContent is not first, the elements have to be swapped
-                        // horizontally. In the case of RTL locales, this means pushing startContent
-                        // to the left, hence the negative number.
-                        -1f
-                    },
-                label = "offset",
-            )
-
-        startContent(
-            Modifier.fillMaxHeight().weight(1f).graphicsLayer {
-                translationX = size.width * animatedOffset
-                alpha = animatedAlpha(animatedOffset)
-            }
-        )
-
-        Box(
-            modifier =
-                Modifier.fillMaxHeight().weight(1f).graphicsLayer {
-                    // A negative sign is used to make sure this is offset in the direction that's
-                    // opposite of the direction that the user switcher is pushed in.
-                    translationX = -size.width * animatedOffset
-                    alpha = animatedAlpha(animatedOffset)
-                }
-        ) {
-            endContent(Modifier.widthIn(max = 400.dp).align(Alignment.BottomCenter))
-        }
-    }
-}
-
-/**
- * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap
- * anywhere on the background to flip their positions.
- *
- * In situations when [isUserSwitcherVisible] is `false`, one of two things may happen: either the
- * UI for the bouncer will be shown on its own, taking up one side, with the other side just being
- * empty space or, if that kind of "stand-alone side-by-side" isn't supported, the standard
- * rendering of the bouncer will be used instead of the side-by-side layout.
- */
-@Composable
-private fun SideBySideLayout(
-    viewModel: BouncerViewModel,
-    dialogFactory: BouncerSceneDialogFactory,
-    isUserSwitcherVisible: Boolean,
-    modifier: Modifier = Modifier,
-) {
-    SwappableLayout(
-        startContent = { startContentModifier ->
-            if (isUserSwitcherVisible) {
-                UserSwitcher(
-                    viewModel = viewModel,
-                    modifier = startContentModifier,
-                )
-            } else {
-                Box(
-                    modifier = startContentModifier,
-                )
-            }
-        },
-        endContent = { endContentModifier ->
-            StandardLayout(
-                viewModel = viewModel,
-                dialogFactory = dialogFactory,
-                modifier = endContentModifier,
-            )
-        },
-        modifier = modifier,
-    )
-}
-
-/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */
-@Composable
-private fun StackedLayout(
-    viewModel: BouncerViewModel,
-    dialogFactory: BouncerSceneDialogFactory,
-    isUserSwitcherVisible: Boolean,
-    modifier: Modifier = Modifier,
-) {
-    Column(
-        modifier = modifier,
-    ) {
-        if (isUserSwitcherVisible) {
-            UserSwitcher(
-                viewModel = viewModel,
-                modifier = Modifier.fillMaxWidth().weight(1f),
-            )
-        }
-
-        StandardLayout(
-            viewModel = viewModel,
-            dialogFactory = dialogFactory,
-            modifier = Modifier.fillMaxWidth().weight(1f),
-        )
-    }
-}
-
-interface BouncerSceneDialogFactory {
-    operator fun invoke(): AlertDialog
-}
-
-/** Enumerates all supported user-input area visibilities. */
-private enum class UserInputAreaVisibility {
-    /**
-     * Only the area where the user enters the input is shown; the area where the input is reflected
-     * back to the user is not shown.
-     */
-    INPUT_ONLY,
-    /**
-     * Only the area where the input is reflected back to the user is shown; the area where the
-     * input is entered by the user is not shown.
-     */
-    OUTPUT_ONLY,
-}
-
-/**
- * Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of
- * the two reaches a stopping point but `0` in the middle of the transition.
- */
-private fun animatedAlpha(
-    offset: Float,
-): Float {
-    // Describes a curve that is made of two parabolic U-shaped curves mirrored horizontally around
-    // the y-axis. The U on the left runs between x = -1 and x = 0 while the U on the right runs
-    // between x = 0 and x = 1.
-    //
-    // The minimum values of the curves are at -0.5 and +0.5.
-    //
-    // Both U curves are vertically scaled such that they reach the points (-1, 1) and (1, 1).
-    //
-    // Breaking it down, it's y = a×(|x|-m)²+b, where:
-    // x: the offset
-    // y: the alpha
-    // m: x-axis center of the parabolic curves, where the minima are.
-    // b: y-axis offset to apply to the entire curve so the animation spends more time with alpha =
-    // 0.
-    // a: amplitude to scale the parabolic curves to reach y = 1 at x = -1, x = 0, and x = +1.
-    val m = 0.5f
-    val b = -0.25
-    val a = (1 - b) / m.pow(2)
-
-    return max(0f, (a * (abs(offset) - m).pow(2) + b).toFloat())
-}
-
-private val SelectedUserImageSize = 190.dp
-private val UserSwitcherDropdownWidth = SelectedUserImageSize + 2 * 29.dp
-private val UserSwitcherDropdownHeight = 60.dp
-
-private object SceneKeys {
-    val ContiguousSceneKey = SceneTransitionLayoutSceneKey("default")
-    val SplitSceneKey = SceneTransitionLayoutSceneKey("split")
-}
-
-private object SceneElements {
-    val AboveFold = ElementKey("above_fold")
-    val BelowFold = ElementKey("below_fold")
-}
-
-private val SceneTransitions = transitions {
-    from(SceneKeys.ContiguousSceneKey, to = SceneKeys.SplitSceneKey) { spec = tween() }
-}
diff --git a/packages/SystemUI/res/anim/instant_fade_out.xml b/packages/SystemUI/res/anim/instant_fade_out.xml
new file mode 100644
index 0000000..800420b
--- /dev/null
+++ b/packages/SystemUI/res/anim/instant_fade_out.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 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.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fromAlpha="1.0"
+    android:toAlpha="0.0"
+    android:interpolator="@android:interpolator/linear_out_slow_in"
+    android:duration="0"/>
+
diff --git a/packages/SystemUI/res/drawable/ksh_key_item_background.xml b/packages/SystemUI/res/drawable/ksh_key_item_background.xml
index 75ff30d..1db48fa 100644
--- a/packages/SystemUI/res/drawable/ksh_key_item_background.xml
+++ b/packages/SystemUI/res/drawable/ksh_key_item_background.xml
@@ -17,5 +17,5 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
         android:shape="rectangle">
     <solid android:color="@color/ksh_key_item_background" />
-    <corners android:radius="2dp" />
+    <corners android:radius="8dp" />
 </shape>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index fcf9638..a005100 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -22,8 +22,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:minHeight="48dp"
-        android:paddingStart="24dp"
-        android:paddingEnd="24dp"
         android:paddingBottom="8dp">
     <ImageView
             android:id="@+id/keyboard_shortcuts_icon"
@@ -57,5 +55,6 @@
             android:layout_alignParentEnd="true"
             android:textSize="14sp"
             android:scrollHorizontally="false"
-            android:layout_centerVertical="true"/>
+            android:layout_centerVertical="true"
+            android:padding="0dp" />
 </com.android.systemui.statusbar.KeyboardShortcutAppItemLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
index 0759990..4f100f6 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
@@ -21,7 +21,5 @@
           android:textSize="14sp"
           android:fontFamily="sans-serif-medium"
           android:importantForAccessibility="yes"
-          android:paddingStart="24dp"
           android:paddingTop="20dp"
-          android:paddingEnd="24dp"
           android:paddingBottom="10dp"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
index a3901d0..f96edbf 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
@@ -18,7 +18,12 @@
 <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:padding="@dimen/ksh_item_padding"
+        android:minWidth="48dp"
+        android:minHeight="32dp"
+        android:paddingLeft="@dimen/ksh_key_view_padding_horizontal"
+        android:paddingRight="@dimen/ksh_key_view_padding_horizontal"
+        android:paddingTop="@dimen/ksh_key_view_padding_vertical"
+        android:paddingBottom="@dimen/ksh_key_view_padding_vertical"
         android:layout_marginStart="@dimen/ksh_item_margin_start"
-        android:scaleType="fitXY"
+        android:scaleType="matrix"
         android:background="@drawable/ksh_key_item_background" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_icon_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_icon_view.xml
deleted file mode 100644
index a037cb2..0000000
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_icon_view.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:padding="@dimen/ksh_item_padding"
-           android:layout_marginLeft="0dp"
-           android:layout_marginRight="0dp"
-           android:scaleType="fitXY"
-           android:tint="?android:attr/textColorPrimary"
-           style="@style/ShortcutItemBackground" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_view.xml
deleted file mode 100644
index 12b4e15..0000000
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_view.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"
-          android:padding="@dimen/ksh_item_padding"
-          android:layout_marginStart="@dimen/ksh_item_margin_start"
-          style="@style/ShortcutItemBackground"
-          android:textColor="?android:attr/textColorPrimary"
-          android:singleLine="false"
-          android:gravity="center"
-          android:textSize="@dimen/ksh_item_text_size"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_plus_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_plus_view.xml
deleted file mode 100644
index 727f2c1..0000000
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_plus_view.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"
-          android:padding="@dimen/ksh_item_padding"
-          android:layout_marginLeft="0dp"
-          android:layout_marginRight="0dp"
-          android:text="+"
-          style="@style/ShortcutItemBackground"
-          android:textColor="?android:attr/textColorPrimary"
-          android:singleLine="true"
-          android:gravity="center"
-          android:textSize="@dimen/ksh_item_text_size"
-          android:textAllCaps="true"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_separator_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_separator_view.xml
new file mode 100644
index 0000000..8772a73
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_separator_view.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:minHeight="32dp"
+    android:padding="@dimen/ksh_item_padding"
+    android:layout_marginLeft="0dp"
+    android:layout_marginRight="0dp"
+    android:text="@string/keyboard_shortcut_join"
+    android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+    android:singleLine="true"
+    android:gravity="center"
+    android:textSize="@dimen/ksh_item_text_size" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_vertical_bar_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_vertical_bar_view.xml
deleted file mode 100644
index 00ef947..0000000
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_vertical_bar_view.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"
-          android:padding="@dimen/ksh_item_padding"
-          android:layout_marginLeft="0dp"
-          android:layout_marginRight="0dp"
-          android:text="|"
-          style="@style/ShortcutItemBackground"
-          android:textColor="?android:attr/textColorPrimary"
-          android:singleLine="true"
-          android:gravity="center"
-          android:textSize="@dimen/ksh_item_text_size"
-          android:textAllCaps="true"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
index b06f7fc..42bbf25 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
@@ -14,14 +14,18 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"
-          android:padding="@dimen/ksh_item_padding"
-          android:layout_marginStart="@dimen/ksh_item_margin_start"
-          android:background="@drawable/ksh_key_item_background"
-          android:textColor="@color/ksh_key_item_color"
-          android:singleLine="true"
-          android:gravity="center"
-          android:textSize="@dimen/ksh_item_text_size"
-          android:textAllCaps="true"/>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="32dp"
+        android:minHeight="32dp"
+        android:paddingLeft="@dimen/ksh_key_view_padding_horizontal"
+        android:paddingRight="@dimen/ksh_key_view_padding_horizontal"
+        android:paddingTop="@dimen/ksh_key_view_padding_vertical"
+        android:paddingBottom="@dimen/ksh_key_view_padding_vertical"
+        android:layout_marginStart="@dimen/ksh_item_margin_start"
+        android:background="@drawable/ksh_key_item_background"
+        android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+        android:singleLine="true"
+        android:gravity="center"
+        android:textSize="@dimen/ksh_item_text_size" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
index dbcd263..61f69c0 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
@@ -70,17 +70,14 @@
     <HorizontalScrollView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:layout_marginStart="49dp"
+        android:layout_marginEnd="0dp"
         android:scrollbars="none">
         <LinearLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:gravity="center_vertical"
             android:orientation="horizontal">
-            <View
-                android:layout_width="0dp"
-                android:layout_height="0dp"
-                android:layout_marginStart="37dp"/>
-
             <Button
                 android:id="@+id/shortcut_system"
                 android:layout_width="wrap_content"
@@ -116,6 +113,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="50dp"
+        android:layout_marginStart="49dp"
+        android:layout_marginEnd="49dp"
         android:layout_gravity="center_horizontal"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textColor="?android:attr/textColorPrimary"
@@ -126,8 +125,10 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="16dp"
-        android:layout_marginStart="25dp"
-        android:layout_marginEnd="25dp">
+        android:layout_marginStart="49dp"
+        android:layout_marginEnd="49dp"
+        android:overScrollMode="never"
+        android:layout_marginBottom="16dp">
         <LinearLayout
             android:id="@+id/keyboard_shortcuts_container"
             android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
index 7aba1cf..9e54ab1 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
@@ -22,6 +22,8 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginEnd="24dp"
         android:orientation="vertical">
         <ScrollView
             android:id="@+id/keyboard_shortcuts_scroll_view"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
index 130472d..02c8c3a 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
@@ -17,7 +17,8 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="250dp"
-    android:layout_height="48dp"
+    android:layout_height="wrap_content"
+    android:minHeight="48dp"
     android:orientation="vertical"
     android:padding="12dp">
     <TextView
diff --git a/packages/SystemUI/res/layout/screen_record_options.xml b/packages/SystemUI/res/layout/screen_record_options.xml
index d9f4b79..8916e42 100644
--- a/packages/SystemUI/res/layout/screen_record_options.xml
+++ b/packages/SystemUI/res/layout/screen_record_options.xml
@@ -47,6 +47,7 @@
             android:layout_weight="0"
             android:layout_gravity="end"
             android:id="@+id/screenrecord_audio_switch"
+            android:contentDescription="@string/screenrecord_audio_label"
             style="@style/ScreenRecord.Switch"
             android:importantForAccessibility="yes"/>
     </LinearLayout>
@@ -79,6 +80,7 @@
             android:minWidth="48dp"
             android:layout_height="48dp"
             android:id="@+id/screenrecord_taps_switch"
+            android:contentDescription="@string/screenrecord_taps_label"
             style="@style/ScreenRecord.Switch"
             android:importantForAccessibility="yes"/>
     </LinearLayout>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 0620355..e124aa0 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -119,9 +119,8 @@
 
     <!-- Keyboard shortcuts colors -->
     <color name="ksh_application_group_color">#fff44336</color>
-    <color name="ksh_key_item_color">@color/material_grey_600</color>
-    <color name="ksh_key_item_background">@color/material_grey_100</color>
-    <color name="ksh_key_item_new_background">@color/transparent</color>
+    <color name="ksh_key_item_color">?androidprv:attr/materialColorOnSurfaceVariant</color>
+    <color name="ksh_key_item_background">?androidprv:attr/materialColorSurfaceContainerHighest</color>
 
     <color name="instant_apps_color">#ff4d5a64</color>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 30392d2..03960d5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -940,8 +940,11 @@
     <!-- Keyboard shortcuts helper -->
     <dimen name="ksh_layout_width">@dimen/match_parent</dimen>
     <dimen name="ksh_item_text_size">14sp</dimen>
-    <dimen name="ksh_item_padding">4dp</dimen>
+    <dimen name="ksh_item_padding">0dp</dimen>
     <dimen name="ksh_item_margin_start">4dp</dimen>
+    <dimen name="ksh_icon_scaled_size">18dp</dimen>
+    <dimen name="ksh_key_view_padding_vertical">4dp</dimen>
+    <dimen name="ksh_key_view_padding_horizontal">12dp</dimen>
 
     <!-- The size of corner radius of the arrow in the onboarding toast. -->
     <dimen name="recents_onboarding_toast_arrow_corner_radius">2dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index daf6cb3..f49d2a1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1773,13 +1773,13 @@
     <!-- Name used to refer to the "Back" key on the keyboard. -->
     <string name="keyboard_key_back">Back</string>
     <!-- Name used to refer to the "Up" arrow key on the keyboard. -->
-    <string name="keyboard_key_dpad_up">Up</string>
+    <string name="keyboard_key_dpad_up">Up arrow</string>
     <!-- Name used to refer to the "Down" arrow key on the keyboard. -->
-    <string name="keyboard_key_dpad_down">Down</string>
+    <string name="keyboard_key_dpad_down">Down arrow</string>
     <!-- Name used to refer to the "Left" arrow key on the keyboard. -->
-    <string name="keyboard_key_dpad_left">Left</string>
+    <string name="keyboard_key_dpad_left">Left arrow</string>
     <!-- Name used to refer to the "Right" arrow key on the keyboard. -->
-    <string name="keyboard_key_dpad_right">Right</string>
+    <string name="keyboard_key_dpad_right">Right arrow</string>
     <!-- Name used to refer to the "Center" arrow key on the keyboard. -->
     <string name="keyboard_key_dpad_center">Center</string>
     <!-- Name used to refer to the "Tab" key on the keyboard. -->
@@ -1834,9 +1834,11 @@
     <string name="keyboard_shortcut_group_system_shortcuts_helper">Keyboard Shortcuts</string>
     <!-- User visible title for the keyboard shortcut that switches to the next hardware keyboard layout. -->
     <string name="keyboard_shortcut_group_system_switch_input">Switch keyboard layout</string>
+    <!-- User visible string that joins different shortcuts in a list, e.g. shortcut1 "or" shortcut2 "or" ... -->
+    <string  name="keyboard_shortcut_join">or</string>
 
-    <!-- Content description for the clear text button in shortcut search list. [CHAR LIMIT=NONE] -->
-    <string name="keyboard_shortcut_clear_text">Clear text</string>
+    <!-- Content description for the clear search button in shortcut search list. [CHAR LIMIT=NONE] -->
+    <string name="keyboard_shortcut_clear_text">Clear search query</string>
     <!-- The title for keyboard shortcut search list [CHAR LIMIT=25] -->
     <string name="keyboard_shortcut_search_list_title">Shortcuts</string>
     <!-- The hint for keyboard shortcut search list [CHAR LIMIT=25] -->
@@ -1852,52 +1854,63 @@
     <!-- The title of current app category in shortcut search list. [CHAR LIMIT=25] -->
     <string name="keyboard_shortcut_search_category_current_app">Current app</string>
 
+    <!-- A11y message when showing keyboard shortcut search results. [CHAR LIMT=NONE] -->
+    <string name="keyboard_shortcut_a11y_show_search_results">Showing search results</string>
+    <!-- A11y message when filtering to "system" keyboard shortcuts.  [CHAR LIMT=NONE] -->
+    <string name="keyboard_shortcut_a11y_filter_system">Showing system shortcuts</string>
+    <!-- A11y message when filtering to "input" keyboard shortcuts.  [CHAR LIMT=NONE] -->
+    <string name="keyboard_shortcut_a11y_filter_input">Showing input shortcuts</string>
+    <!-- A11y message when filtering to "app opening" keyboard shortcuts.  [CHAR LIMT=NONE] -->
+    <string name="keyboard_shortcut_a11y_filter_open_apps">Showing shortcuts that open apps</string>
+    <!-- A11y message when filtering to "current app" keyboard shortcuts.  [CHAR LIMT=NONE] -->
+    <string name="keyboard_shortcut_a11y_filter_current_app">Showing shortcuts for the current app</string>
+
     <!-- User visible title for the keyboard shortcut that triggers the notification shade. [CHAR LIMIT=70] -->
-    <string name="group_system_access_notification_shade">Access notification shade</string>
+    <string name="group_system_access_notification_shade">View notifications</string>
     <!-- User visible title for the keyboard shortcut that takes a full screenshot. [CHAR LIMIT=70] -->
-    <string name="group_system_full_screenshot">Take a full screenshot</string>
+    <string name="group_system_full_screenshot">Take screenshot</string>
     <!-- User visible title for the keyboard shortcut that access list of system / apps shortcuts. [CHAR LIMIT=70] -->
-    <string name="group_system_access_system_app_shortcuts">Access list of system / apps shortcuts</string>
+    <string name="group_system_access_system_app_shortcuts">Show shortcuts</string>
     <!-- User visible title for the keyboard shortcut that goes back to previous state. [CHAR LIMIT=70] -->
-    <string name="group_system_go_back">Back: go back to previous state (back button)</string>
+    <string name="group_system_go_back">Go back</string>
     <!-- User visible title for the keyboard shortcut that takes the user to the home screen. [CHAR LIMIT=70] -->
-    <string name="group_system_access_home_screen">Access home screen</string>
+    <string name="group_system_access_home_screen">Go to home screen</string>
     <!-- User visible title for the keyboard shortcut that triggers overview of open apps. [CHAR LIMIT=70] -->
-    <string name="group_system_overview_open_apps">Overview of open apps</string>
+    <string name="group_system_overview_open_apps">View recent apps</string>
     <!-- User visible title for the keyboard shortcut that cycles through recent apps (forward). [CHAR LIMIT=70] -->
-    <string name="group_system_cycle_forward">Cycle through recent apps (forward)</string>
+    <string name="group_system_cycle_forward">Cycle forward through recent apps</string>
     <!-- User visible title for the keyboard shortcut that cycles through recent apps (back). [CHAR LIMIT=70] -->
-    <string name="group_system_cycle_back">Cycle through recent apps (back)</string>
+    <string name="group_system_cycle_back">Cycle backward through recent apps</string>
     <!-- User visible title for the keyboard shortcut that accesses list of all apps and search. [CHAR LIMIT=70] -->
-    <string name="group_system_access_all_apps_search">Access list of all apps and search (i.e. Search/Launcher)</string>
+    <string name="group_system_access_all_apps_search">Open apps list</string>
     <!-- User visible title for the keyboard shortcut that hides and (re)showes taskbar. [CHAR LIMIT=70] -->
-    <string name="group_system_hide_reshow_taskbar">Hide and (re)show taskbar</string>
-    <!-- User visible title for the keyboard shortcut that accesses system settings. [CHAR LIMIT=70] -->
-    <string name="group_system_access_system_settings">Access system settings</string>
-    <!-- User visible title for the keyboard shortcut that accesses Google Assistant. [CHAR LIMIT=70] -->
-    <string name="group_system_access_google_assistant">Access Google Assistant</string>
+    <string name="group_system_hide_reshow_taskbar">Show taskbar</string>
+    <!-- User visible title for the keyboard shortcut that accesses [system] settings. [CHAR LIMIT=70] -->
+    <string name="group_system_access_system_settings">Open settings</string>
+    <!-- User visible title for the keyboard shortcut that accesses Assistant app. [CHAR LIMIT=70] -->
+    <string name="group_system_access_google_assistant">Open assistant</string>
     <!-- User visible title for the keyboard shortcut that locks screen. [CHAR LIMIT=70] -->
     <string name="group_system_lock_screen">Lock screen</string>
     <!-- User visible title for the keyboard shortcut that pulls up Notes app for quick memo. [CHAR LIMIT=70] -->
-    <string name="group_system_quick_memo">Pull up Notes app for quick memo</string>
+    <string name="group_system_quick_memo">Open notes</string>
 
     <!-- User visible title for the system multitasking keyboard shortcuts list. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_system_multitasking">System multitasking</string>
     <!-- User visible title for the keyboard shortcut that enters split screen with current app to RHS [CHAR LIMIT=70] -->
-    <string name="system_multitasking_rhs">Enter Split screen with current app to RHS</string>
+    <string name="system_multitasking_rhs">Enter split screen with current app to RHS</string>
     <!-- User visible title for the keyboard shortcut that enters split screen with current app to LHS [CHAR LIMIT=70] -->
-    <string name="system_multitasking_lhs">Enter Split screen with current app to LHS</string>
+    <string name="system_multitasking_lhs">Enter split screen with current app to LHS</string>
     <!-- User visible title for the keyboard shortcut that switches from split screen to full screen [CHAR LIMIT=70] -->
-    <string name="system_multitasking_full_screen">Switch from Split screen to full screen</string>
+    <string name="system_multitasking_full_screen">Switch from split screen to full screen</string>
     <!-- User visible title for the keyboard shortcut that replaces an app from one to another during split screen [CHAR LIMIT=70] -->
-    <string name="system_multitasking_replace">During Split screen: replace an app from one to another</string>
+    <string name="system_multitasking_replace">During split screen: replace an app from one to another</string>
 
     <!-- User visible title for the input keyboard shortcuts list. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_input">Input</string>
     <!-- User visible title for the keyboard shortcut that switches input language (next language). [CHAR LIMIT=70] -->
-    <string name="input_switch_input_language_next">Switch input language (next language)</string>
+    <string name="input_switch_input_language_next">Switch to next language</string>
     <!-- User visible title for the keyboard shortcut that switches input language (previous language). [CHAR LIMIT=70] -->
-    <string name="input_switch_input_language_previous">Switch input language (previous language)</string>
+    <string name="input_switch_input_language_previous">Switch to previous language</string>
     <!-- User visible title for the keyboard shortcut that accesses emoji. [CHAR LIMIT=70] -->
     <string name="input_access_emoji">Access emoji</string>
     <!-- User visible title for the keyboard shortcut that accesses voice typing. [CHAR LIMIT=70] -->
@@ -1905,14 +1918,14 @@
 
     <!-- User visible title for the system-wide applications keyboard shortcuts list. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications">Applications</string>
-    <!-- User visible title for the keyboard shortcut that takes the user to the assist app. [CHAR LIMIT=70] -->
-    <string name="keyboard_shortcut_group_applications_assist">Assist</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the assistant app. [CHAR LIMIT=70] -->
+    <string name="keyboard_shortcut_group_applications_assist">Assistant</string>
     <!-- User visible title for the keyboard shortcut that takes the user to the browser app. [CHAR LIMIT=70] -->
-    <string name="keyboard_shortcut_group_applications_browser">Browser (Chrome as default)</string>
+    <string name="keyboard_shortcut_group_applications_browser">Browser</string>
     <!-- User visible title for the keyboard shortcut that takes the user to the contacts app. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications_contacts">Contacts</string>
     <!-- User visible title for the keyboard shortcut that takes the user to the email app. [CHAR LIMIT=70] -->
-    <string name="keyboard_shortcut_group_applications_email">Email (Gmail as default)</string>
+    <string name="keyboard_shortcut_group_applications_email">Email</string>
     <!-- User visible title for the keyboard shortcut that takes the user to the SMS messaging app. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications_sms">SMS</string>
     <!-- User visible title for the keyboard shortcut that takes the user to the music app. [CHAR LIMIT=70] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index befee2b..c48dd9d 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -420,6 +420,11 @@
     <!-- Cannot double inherit. Use Theme.SystemUI.QuickSettings in code to match -->
     <style name="BrightnessDialog" parent="@android:style/Theme.DeviceDefault.Dialog">
         <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowAnimationStyle">@style/Animation.BrightnessDialog</item>
+    </style>
+
+    <style name="Animation.BrightnessDialog">
+        <item name="android:windowExitAnimation">@anim/instant_fade_out</item>
     </style>
 
     <style name="Theme.SystemUI.ContrastDialog" parent="@android:style/Theme.DeviceDefault.Dialog">
@@ -1513,10 +1518,6 @@
         <item name="android:background">?android:attr/dividerHorizontal</item>
     </style>
 
-    <style name="ShortcutItemBackground">
-        <item name="android:background">@color/ksh_key_item_new_background</item>
-    </style>
-
     <style name="LongPressLockScreenAnimation">
         <item name="android:windowEnterAnimation">@anim/long_press_lock_screen_popup_enter</item>
         <item name="android:windowExitAnimation">@anim/long_press_lock_screen_popup_exit</item>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
index 050b399..ff23837 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -30,13 +30,16 @@
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import java.util.concurrent.Executor
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.stateIn
 
 /** Repository for the current state of the display */
@@ -66,7 +69,8 @@
     deviceStateManager: DeviceStateManager,
     displayManager: DisplayManager,
     @Main handler: Handler,
-    @Main mainExecutor: Executor
+    @Background backgroundExecutor: Executor,
+    @Background backgroundDispatcher: CoroutineDispatcher,
 ) : DisplayStateRepository {
     override val isReverseDefaultRotation =
         context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
@@ -94,9 +98,10 @@
                     }
 
                 sendRearDisplayStateUpdate(false)
-                deviceStateManager.registerCallback(mainExecutor, callback)
+                deviceStateManager.registerCallback(backgroundExecutor, callback)
                 awaitClose { deviceStateManager.unregisterCallback(callback) }
             }
+            .flowOn(backgroundDispatcher)
             .stateIn(
                 applicationScope,
                 started = SharingStarted.Eagerly,
@@ -137,6 +142,7 @@
                 )
                 awaitClose { displayManager.unregisterDisplayListener(callback) }
             }
+            .flowOn(backgroundDispatcher)
             .stateIn(
                 applicationScope,
                 started = SharingStarted.Eagerly,
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
index 7bca86e..12be32c 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
@@ -31,8 +31,10 @@
 import com.android.systemui.util.kotlin.emitOnStart
 import com.android.systemui.util.view.bindLatest
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 
@@ -95,7 +97,8 @@
  * call [onInflate] on the resulting view each time. Disposes of the [DisposableHandle] returned by
  * [onInflate] when done.
  *
- * This never completes unless cancelled, it just suspends and waits for updates.
+ * This never completes unless cancelled, it just suspends and waits for updates. It runs on a
+ * background thread using [backgroundDispatcher].
  *
  * For parameters [resource], [root] and [attachToRoot], see [LayoutInflater.inflate].
  *
@@ -105,7 +108,7 @@
  * ```
  * parentView.repeatWhenAttached {
  *     configurationState
- *         .reinflateOnChange(
+ *         .reinflateAndBindLatest(
  *             R.layout.my_layout,
  *             parentView,
  *             attachToRoot = false,
@@ -124,7 +127,10 @@
     @LayoutRes resource: Int,
     root: ViewGroup?,
     attachToRoot: Boolean,
+    backgroundDispatcher: CoroutineDispatcher,
     onInflate: (T) -> DisposableHandle?,
 ) {
-    inflateLayout<T>(resource, root, attachToRoot).bindLatest(onInflate)
+    inflateLayout<T>(resource, root, attachToRoot)
+        .flowOn(backgroundDispatcher)
+        .bindLatest(onInflate)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 2b1cdc2..33dd3d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -647,7 +647,7 @@
         public void dismissKeyguardToLaunch(Intent intentToLaunch) {
             trace("dismissKeyguardToLaunch");
             checkPermission();
-            mKeyguardViewMediator.dismissKeyguardToLaunch(intentToLaunch);
+            Slog.d(TAG, "Ignoring dismissKeyguardToLaunch " + intentToLaunch);
         }
 
         @Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 53ec3de..3009087 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -248,7 +248,6 @@
     private static final int SHOW = 1;
     private static final int HIDE = 2;
     private static final int RESET = 3;
-    private static final int VERIFY_UNLOCK = 4;
     private static final int NOTIFY_FINISHED_GOING_TO_SLEEP = 5;
     private static final int KEYGUARD_DONE = 7;
     private static final int KEYGUARD_DONE_DRAWING = 8;
@@ -2316,15 +2315,6 @@
         mHandler.sendMessage(msg);
     }
 
-    /**
-     * Send message to keyguard telling it to verify unlock
-     * @see #handleVerifyUnlock()
-     */
-    private void verifyUnlockLocked() {
-        if (DEBUG) Log.d(TAG, "verifyUnlockLocked");
-        mHandler.sendEmptyMessage(VERIFY_UNLOCK);
-    }
-
     private void notifyStartedGoingToSleep() {
         if (DEBUG) Log.d(TAG, "notifyStartedGoingToSleep");
         mHandler.sendEmptyMessage(NOTIFY_STARTED_GOING_TO_SLEEP);
@@ -2498,12 +2488,6 @@
                     message = "RESET";
                     handleReset(msg.arg1 != 0);
                     break;
-                case VERIFY_UNLOCK:
-                    message = "VERIFY_UNLOCK";
-                    Trace.beginSection("KeyguardViewMediator#handleMessage VERIFY_UNLOCK");
-                    handleVerifyUnlock();
-                    Trace.endSection();
-                    break;
                 case NOTIFY_STARTED_GOING_TO_SLEEP:
                     message = "NOTIFY_STARTED_GOING_TO_SLEEP";
                     handleNotifyStartedGoingToSleep();
@@ -3435,20 +3419,6 @@
         scheduleNonStrongBiometricIdleTimeout();
     }
 
-    /**
-     * Handle message sent by {@link #verifyUnlock}
-     * @see #VERIFY_UNLOCK
-     */
-    private void handleVerifyUnlock() {
-        Trace.beginSection("KeyguardViewMediator#handleVerifyUnlock");
-        synchronized (KeyguardViewMediator.this) {
-            if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
-            setShowingLocked(true);
-            mKeyguardViewControllerLazy.get().dismissAndCollapse();
-        }
-        Trace.endSection();
-    }
-
     private void handleNotifyStartedGoingToSleep() {
         synchronized (KeyguardViewMediator.this) {
             if (DEBUG) Log.d(TAG, "handleNotifyStartedGoingToSleep");
@@ -3606,10 +3576,6 @@
         // do nothing
     }
 
-    public void dismissKeyguardToLaunch(Intent intentToLaunch) {
-        // do nothing
-    }
-
     public void onSystemKeyPressed(int keycode) {
         // do nothing
     }
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 8ef2662..36412e3 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
@@ -253,7 +253,7 @@
                     Pair(isAuthenticated.isFalse(), "faceNotAuthenticated"),
                 )
                 .andAllFlows("canFaceAuthRun", faceAuthLog)
-                .flowOn(mainDispatcher)
+                .flowOn(backgroundDispatcher)
                 .stateIn(applicationScope, SharingStarted.Eagerly, false)
 
         // Face detection can run only when lockscreen bypass is enabled
@@ -280,7 +280,7 @@
                     )
                 )
                 .andAllFlows("canFaceDetectRun", faceDetectLog)
-                .flowOn(mainDispatcher)
+                .flowOn(backgroundDispatcher)
                 .stateIn(applicationScope, SharingStarted.Eagerly, false)
         observeFaceAuthGatingChecks()
         observeFaceDetectGatingChecks()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 9bec300..96386f3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
@@ -33,11 +34,13 @@
 import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates state about device entry fingerprint auth mechanism. */
@@ -74,6 +77,7 @@
     val authController: AuthController,
     val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     @Application scope: CoroutineScope,
+    @Main private val mainDispatcher: CoroutineDispatcher,
 ) : DeviceEntryFingerprintAuthRepository {
 
     override val availableFpSensorType: Flow<BiometricType?>
@@ -137,30 +141,34 @@
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
 
     override val isRunning: Flow<Boolean>
-        get() = conflatedCallbackFlow {
-            val callback =
-                object : KeyguardUpdateMonitorCallback() {
-                    override fun onBiometricRunningStateChanged(
-                        running: Boolean,
-                        biometricSourceType: BiometricSourceType?
-                    ) {
-                        if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
-                            trySendWithFailureLogging(
-                                running,
-                                TAG,
-                                "Fingerprint running state changed"
-                            )
+        get() =
+            conflatedCallbackFlow {
+                    val callback =
+                        object : KeyguardUpdateMonitorCallback() {
+                            override fun onBiometricRunningStateChanged(
+                                running: Boolean,
+                                biometricSourceType: BiometricSourceType?
+                            ) {
+                                if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
+                                    trySendWithFailureLogging(
+                                        running,
+                                        TAG,
+                                        "Fingerprint running state changed"
+                                    )
+                                }
+                            }
                         }
-                    }
+                    keyguardUpdateMonitor.registerCallback(callback)
+                    trySendWithFailureLogging(
+                        keyguardUpdateMonitor.isFingerprintDetectionRunning,
+                        TAG,
+                        "Initial fingerprint running state"
+                    )
+                    awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
                 }
-            keyguardUpdateMonitor.registerCallback(callback)
-            trySendWithFailureLogging(
-                keyguardUpdateMonitor.isFingerprintDetectionRunning,
-                TAG,
-                "Initial fingerprint running state"
-            )
-            awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
-        }
+                .flowOn(
+                    mainDispatcher
+                ) // keyguardUpdateMonitor requires registration on main thread.
 
     override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
         get() = conflatedCallbackFlow {
@@ -171,7 +179,6 @@
                         biometricSourceType: BiometricSourceType,
                         isStrongBiometric: Boolean,
                     ) {
-
                         sendUpdateIfFingerprint(
                             biometricSourceType,
                             SuccessFingerprintAuthenticationStatus(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt
index adb1e01..7c43092 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt
@@ -19,11 +19,14 @@
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.shared.model.DevicePosture
 import com.android.systemui.statusbar.policy.DevicePostureController
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
 
 /** Provide current device posture state. */
 interface DevicePostureRepository {
@@ -34,23 +37,28 @@
 @SysUISingleton
 class DevicePostureRepositoryImpl
 @Inject
-constructor(private val postureController: DevicePostureController) : DevicePostureRepository {
+constructor(
+    private val postureController: DevicePostureController,
+    @Main private val mainDispatcher: CoroutineDispatcher
+) : DevicePostureRepository {
     override val currentDevicePosture: Flow<DevicePosture>
-        get() = conflatedCallbackFlow {
-            val sendPostureUpdate = { posture: Int ->
-                val currentDevicePosture = DevicePosture.toPosture(posture)
-                trySendWithFailureLogging(
-                    currentDevicePosture,
-                    TAG,
-                    "Error sending posture update to $currentDevicePosture"
-                )
-            }
-            val callback = DevicePostureController.Callback { sendPostureUpdate(it) }
-            postureController.addCallback(callback)
-            sendPostureUpdate(postureController.devicePosture)
+        get() =
+            conflatedCallbackFlow {
+                    val sendPostureUpdate = { posture: Int ->
+                        val currentDevicePosture = DevicePosture.toPosture(posture)
+                        trySendWithFailureLogging(
+                            currentDevicePosture,
+                            TAG,
+                            "Error sending posture update to $currentDevicePosture"
+                        )
+                    }
+                    val callback = DevicePostureController.Callback { sendPostureUpdate(it) }
+                    postureController.addCallback(callback)
+                    sendPostureUpdate(postureController.devicePosture)
 
-            awaitClose { postureController.removeCallback(callback) }
-        }
+                    awaitClose { postureController.removeCallback(callback) }
+                }
+                .flowOn(mainDispatcher) // DevicePostureController requirement
 
     companion object {
         const val TAG = "PostureRepositoryImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 4da48f6..706aba3c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -302,4 +302,11 @@
     fun isFinishedInState(state: KeyguardState): Flow<Boolean> {
         return finishedKeyguardState.map { it == state }.distinctUntilChanged()
     }
+
+    /**
+     * Whether we've FINISHED a transition to a state that matches the given predicate. Consider
+     * using [isFinishedInStateWhere] whenever possible instead
+     */
+    fun isFinishedInStateWhereValue(stateMatcher: (KeyguardState) -> Boolean) =
+        stateMatcher(finishedKeyguardState.replayCache.last())
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 0c5a14f..48f432e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -58,8 +58,8 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.systemui.res.R;
 import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import java.util.concurrent.Executor;
@@ -85,6 +85,13 @@
     final MediaOutputController mMediaOutputController;
     final BroadcastSender mBroadcastSender;
 
+    /**
+     * Signals whether the dialog should NOT show app-related metadata.
+     *
+     * <p>A metadata-less dialog hides the title, subtitle, and app icon in the header.
+     */
+    private final boolean mIncludePlaybackAndAppMetadata;
+
     @VisibleForTesting
     View mDialogView;
     private TextView mHeaderTitle;
@@ -210,8 +217,11 @@
         }
     }
 
-    public MediaOutputBaseDialog(Context context, BroadcastSender broadcastSender,
-            MediaOutputController mediaOutputController) {
+    public MediaOutputBaseDialog(
+            Context context,
+            BroadcastSender broadcastSender,
+            MediaOutputController mediaOutputController,
+            boolean includePlaybackAndAppMetadata) {
         super(context, R.style.Theme_SystemUI_Dialog_Media);
 
         // Save the context that is wrapped with our theme.
@@ -226,6 +236,7 @@
         mListPaddingTop = mContext.getResources().getDimensionPixelSize(
                 R.dimen.media_output_dialog_list_padding_top);
         mExecutor = Executors.newSingleThreadExecutor();
+        mIncludePlaybackAndAppMetadata = includePlaybackAndAppMetadata;
     }
 
     @Override
@@ -354,7 +365,10 @@
             updateDialogBackgroundColor();
             mHeaderIcon.setVisibility(View.GONE);
         }
-        if (appSourceIcon != null) {
+
+        if (!mIncludePlaybackAndAppMetadata) {
+            mAppResourceIcon.setVisibility(View.GONE);
+        } else if (appSourceIcon != null) {
             Icon appIcon = appSourceIcon.toIcon(mContext);
             mAppResourceIcon.setColorFilter(mMediaOutputController.getColorItemContent());
             mAppResourceIcon.setImageIcon(appIcon);
@@ -373,17 +387,24 @@
             mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size));
         }
         mAppButton.setText(mMediaOutputController.getAppSourceName());
-        // Update title and subtitle
-        mHeaderTitle.setText(getHeaderText());
-        final CharSequence subTitle = getHeaderSubtitle();
-        if (TextUtils.isEmpty(subTitle)) {
+
+        if (!mIncludePlaybackAndAppMetadata) {
+            mHeaderTitle.setVisibility(View.GONE);
             mHeaderSubtitle.setVisibility(View.GONE);
-            mHeaderTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
         } else {
-            mHeaderSubtitle.setVisibility(View.VISIBLE);
-            mHeaderSubtitle.setText(subTitle);
-            mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
+            // Update title and subtitle
+            mHeaderTitle.setText(getHeaderText());
+            final CharSequence subTitle = getHeaderSubtitle();
+            if (TextUtils.isEmpty(subTitle)) {
+                mHeaderSubtitle.setVisibility(View.GONE);
+                mHeaderTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+            } else {
+                mHeaderSubtitle.setVisibility(View.VISIBLE);
+                mHeaderSubtitle.setText(subTitle);
+                mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
+            }
         }
+
         // Show when remote media session is available or
         //      when the device supports BT LE audio + media is playing
         mStopButton.setVisibility(getStopButtonVisibility());
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index ac64300..8e0191e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -42,12 +42,10 @@
 import androidx.core.graphics.drawable.IconCompat;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.media.BluetoothMediaDevice;
-import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.qrcode.QrCodeGenerator;
-import com.android.systemui.res.R;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import com.google.zxing.WriterException;
@@ -237,7 +235,11 @@
 
     MediaOutputBroadcastDialog(Context context, boolean aboveStatusbar,
             BroadcastSender broadcastSender, MediaOutputController mediaOutputController) {
-        super(context, broadcastSender, mediaOutputController);
+        super(
+                context,
+                broadcastSender,
+                mediaOutputController, /* includePlaybackAndAppMetadata */
+                true);
         mAdapter = new MediaOutputAdapter(mMediaOutputController);
         // TODO(b/226710953): Move the part to MediaOutputBaseDialog for every class
         //  that extends MediaOutputBaseDialog
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 426a497..375a0ce 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -78,7 +78,6 @@
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.res.R;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
@@ -86,6 +85,7 @@
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.monet.ColorScheme;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -358,7 +358,7 @@
     }
 
     Drawable getAppSourceIconFromPackage() {
-        if (mPackageName.isEmpty()) {
+        if (TextUtils.isEmpty(mPackageName)) {
             return null;
         }
         try {
@@ -372,7 +372,7 @@
     }
 
     String getAppSourceName() {
-        if (mPackageName.isEmpty()) {
+        if (TextUtils.isEmpty(mPackageName)) {
             return null;
         }
         final PackageManager packageManager = mContext.getPackageManager();
@@ -391,7 +391,7 @@
     }
 
     Intent getAppLaunchIntent() {
-        if (mPackageName.isEmpty()) {
+        if (TextUtils.isEmpty(mPackageName)) {
             return null;
         }
         return mContext.getPackageManager().getLaunchIntentForPackage(mPackageName);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 4640a5d..d40699c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -27,10 +27,10 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.res.R;
 
 /**
  * Dialog for media output transferring.
@@ -40,10 +40,15 @@
     private final DialogLaunchAnimator mDialogLaunchAnimator;
     private final UiEventLogger mUiEventLogger;
 
-    MediaOutputDialog(Context context, boolean aboveStatusbar, BroadcastSender broadcastSender,
-            MediaOutputController mediaOutputController, DialogLaunchAnimator dialogLaunchAnimator,
-            UiEventLogger uiEventLogger) {
-        super(context, broadcastSender, mediaOutputController);
+    MediaOutputDialog(
+            Context context,
+            boolean aboveStatusbar,
+            BroadcastSender broadcastSender,
+            MediaOutputController mediaOutputController,
+            DialogLaunchAnimator dialogLaunchAnimator,
+            UiEventLogger uiEventLogger,
+            boolean includePlaybackAndAppMetadata) {
+        super(context, broadcastSender, mediaOutputController, includePlaybackAndAppMetadata);
         mDialogLaunchAnimator = dialogLaunchAnimator;
         mUiEventLogger = uiEventLogger;
         mAdapter = new MediaOutputAdapter(mMediaOutputController);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 2b38edb..b04a7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -61,6 +61,19 @@
 
     /** Creates a [MediaOutputDialog] for the given package. */
     open fun create(packageName: String, aboveStatusBar: Boolean, view: View? = null) {
+        create(packageName, aboveStatusBar, view, includePlaybackAndAppMetadata = true)
+    }
+
+    open fun createDialogForSystemRouting() {
+        create(packageName = null, aboveStatusBar = false, includePlaybackAndAppMetadata = false)
+    }
+
+    private fun create(
+            packageName: String?,
+            aboveStatusBar: Boolean,
+            view: View? = null,
+            includePlaybackAndAppMetadata: Boolean = true
+    ) {
         // Dismiss the previous dialog, if any.
         mediaOutputDialog?.dismiss()
 
@@ -71,7 +84,7 @@
             powerExemptionManager, keyGuardManager, featureFlags, userTracker)
         val dialog =
             MediaOutputDialog(context, aboveStatusBar, broadcastSender, controller,
-                    dialogLaunchAnimator, uiEventLogger)
+                    dialogLaunchAnimator, uiEventLogger, includePlaybackAndAppMetadata)
         mediaOutputDialog = dialog
 
         // Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
index 132bf99..1002cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
@@ -19,7 +19,6 @@
 import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
-import android.text.TextUtils
 import android.util.Log
 import com.android.settingslib.media.MediaOutputConstants
 import javax.inject.Inject
@@ -35,16 +34,16 @@
     private val mediaOutputBroadcastDialogFactory: MediaOutputBroadcastDialogFactory
 ) : BroadcastReceiver() {
     override fun onReceive(context: Context, intent: Intent) {
-        when {
-            TextUtils.equals(
-                MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG, intent.action) -> {
+        when (intent.action) {
+            MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG -> {
                 val packageName: String? =
                     intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
                 launchMediaOutputDialogIfPossible(packageName)
             }
-            TextUtils.equals(
-                MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG,
-                intent.action) -> {
+            MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG -> {
+                mediaOutputDialogFactory.createDialogForSystemRouting()
+            }
+            MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG -> {
                 val packageName: String? =
                     intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
                 launchMediaOutputBroadcastDialogIfPossible(packageName)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
index 654fffe8..1983a67 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
@@ -24,6 +24,7 @@
 import android.view.ViewGroup
 import android.view.ViewStub
 import android.view.WindowManager
+import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.AdapterView
 import android.widget.ArrayAdapter
 import android.widget.ImageView
@@ -106,6 +107,19 @@
         screenShareModeSpinner = dialog.requireViewById(R.id.screen_share_mode_spinner)
         screenShareModeSpinner.adapter = adapter
         screenShareModeSpinner.onItemSelectedListener = this
+
+        // disable redundant Touch & Hold accessibility action for Switch Access
+        screenShareModeSpinner.accessibilityDelegate =
+            object : View.AccessibilityDelegate() {
+                override fun onInitializeAccessibilityNodeInfo(
+                    host: View,
+                    info: AccessibilityNodeInfo
+                ) {
+                    info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)
+                    super.onInitializeAccessibilityNodeInfo(host, info)
+                }
+            }
+        screenShareModeSpinner.isLongClickable = false
     }
 
     override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index f13add9..e2e94da 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -72,6 +72,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.provider.DeviceConfig;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
@@ -736,17 +737,27 @@
     }
 
     public void destroyView() {
-        setAutoHideController(/* autoHideController */ null);
-        mCommandQueue.removeCallback(this);
-        mWindowManager.removeViewImmediate(mView.getRootView());
-        mNavigationModeController.removeListener(mModeChangedListener);
-        mEdgeBackGestureHandler.setStateChangeCallback(null);
+        Trace.beginSection("NavigationBar#destroyView");
+        try {
+            setAutoHideController(/* autoHideController */ null);
+            mCommandQueue.removeCallback(this);
+            Trace.beginSection("NavigationBar#removeViewImmediate");
+            try {
+                mWindowManager.removeViewImmediate(mView.getRootView());
+            } finally {
+                Trace.endSection();
+            }
+            mNavigationModeController.removeListener(mModeChangedListener);
+            mEdgeBackGestureHandler.setStateChangeCallback(null);
 
-        mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
-        mNotificationShadeDepthController.removeListener(mDepthListener);
+            mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+            mNotificationShadeDepthController.removeListener(mDepthListener);
 
-        mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
-        mTaskStackChangeListeners.unregisterTaskStackListener(mTaskStackListener);
+            mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
+            mTaskStackChangeListeners.unregisterTaskStackListener(mTaskStackListener);
+        } finally {
+            Trace.endSection();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 3b32313e..8d1ff98a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -46,6 +46,7 @@
 import android.inputmethodservice.InputMethodService;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.util.Log;
 import android.view.Display;
 import android.view.View;
@@ -229,28 +230,34 @@
     }
 
     public void init(int displayId) {
-        if (mInitialized) {
-            return;
+        Trace.beginSection("TaskbarDelegate#init");
+        try {
+            if (mInitialized) {
+                return;
+            }
+            mDisplayId = displayId;
+            parseCurrentSysuiState();
+            mCommandQueue.addCallback(this);
+            mOverviewProxyService.addCallback(this);
+            onNavigationModeChanged(mNavigationModeController.addListener(this));
+            mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+            // Initialize component callback
+            Display display = mDisplayManager.getDisplay(displayId);
+            mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
+            mScreenPinningNotify = new ScreenPinningNotify(mWindowContext);
+            // Set initial state for any listeners
+            updateSysuiFlags();
+            mAutoHideController.setNavigationBar(mAutoHideUiElement);
+            mLightBarController.setNavigationBar(mLightBarTransitionsController);
+            mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
+            mEdgeBackGestureHandler.setBackAnimation(mBackAnimation);
+            mEdgeBackGestureHandler.onConfigurationChanged(
+                    mContext.getResources().getConfiguration());
+            mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
+            mInitialized = true;
+        } finally {
+            Trace.endSection();
         }
-        mDisplayId = displayId;
-        parseCurrentSysuiState();
-        mCommandQueue.addCallback(this);
-        mOverviewProxyService.addCallback(this);
-        onNavigationModeChanged(mNavigationModeController.addListener(this));
-        mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
-        // Initialize component callback
-        Display display = mDisplayManager.getDisplay(displayId);
-        mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
-        mScreenPinningNotify = new ScreenPinningNotify(mWindowContext);
-        // Set initial state for any listeners
-        updateSysuiFlags();
-        mAutoHideController.setNavigationBar(mAutoHideUiElement);
-        mLightBarController.setNavigationBar(mLightBarTransitionsController);
-        mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
-        mEdgeBackGestureHandler.setBackAnimation(mBackAnimation);
-        mEdgeBackGestureHandler.onConfigurationChanged(mContext.getResources().getConfiguration());
-        mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
-        mInitialized = true;
     }
 
     public void destroy() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 3dfd292..9846f4b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -578,10 +578,15 @@
      * @see NavigationModeController.ModeChangedListener#onNavigationModeChanged
      */
     public void onNavigationModeChanged(int mode) {
-        mUsingThreeButtonNav = QuickStepContract.isLegacyMode(mode);
-        mInGestureNavMode = QuickStepContract.isGesturalMode(mode);
-        updateIsEnabled();
-        updateCurrentUserResources();
+        Trace.beginSection("EdgeBackGestureHandler#onNavigationModeChanged");
+        try {
+            mUsingThreeButtonNav = QuickStepContract.isLegacyMode(mode);
+            mInGestureNavMode = QuickStepContract.isGesturalMode(mode);
+            updateIsEnabled();
+            updateCurrentUserResources();
+        } finally {
+            Trace.endSection();
+        }
     }
 
     public void onNavBarTransientStateChanged(boolean isTransient) {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index d9a8080..270bfbe 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -37,9 +37,10 @@
 import android.provider.Settings
 import android.widget.Toast
 import androidx.annotation.VisibleForTesting
-import com.android.systemui.res.R
+import com.android.app.tracing.TraceUtils.Companion.launch
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
 import com.android.systemui.log.DebugLogger.debugLog
 import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
@@ -47,6 +48,7 @@
 import com.android.systemui.notetask.NoteTaskRoleManagerExt.createNoteShortcutInfoAsUser
 import com.android.systemui.notetask.NoteTaskRoleManagerExt.getDefaultRoleHolderAsUser
 import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
 import com.android.systemui.util.settings.SecureSettings
@@ -54,8 +56,8 @@
 import com.android.wm.shell.bubbles.Bubbles.BubbleExpandListener
 import java.util.concurrent.atomic.AtomicReference
 import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
 
 /**
  * Entry point for creating and managing note.
@@ -81,7 +83,8 @@
     private val devicePolicyManager: DevicePolicyManager,
     private val userTracker: UserTracker,
     private val secureSettings: SecureSettings,
-    @Application private val applicationScope: CoroutineScope
+    @Application private val applicationScope: CoroutineScope,
+    @Background private val bgCoroutineContext: CoroutineContext
 ) {
 
     @VisibleForTesting val infoReference = AtomicReference<NoteTaskInfo?>()
@@ -172,7 +175,9 @@
     ) {
         if (!isEnabled) return
 
-        applicationScope.launch { awaitShowNoteTaskAsUser(entryPoint, user) }
+        applicationScope.launch("$TAG#showNoteTaskAsUser") {
+            awaitShowNoteTaskAsUser(entryPoint, user)
+        }
     }
 
     private suspend fun awaitShowNoteTaskAsUser(
@@ -337,7 +342,7 @@
 
     @InternalNoteTaskApi
     fun launchUpdateNoteTaskAsUser(user: UserHandle) {
-        applicationScope.launch {
+        applicationScope.launch("$TAG#launchUpdateNoteTaskAsUser", bgCoroutineContext) {
             if (!userManager.isUserUnlocked(user)) {
                 debugLog { "updateNoteTaskAsUserInternal call but user locked: user=$user" }
                 return@launch
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index f8e0159..4565200 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -45,6 +45,7 @@
 import android.widget.Switch
 import android.widget.TextView
 import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.traceSection
 import com.android.settingslib.Utils
 import com.android.systemui.FontSizeUtils
 import com.android.systemui.animation.LaunchableView
@@ -707,7 +708,7 @@
 
     inner class StateChangeRunnable(private val state: QSTile.State) : Runnable {
         override fun run() {
-            handleStateChanged(state)
+            traceSection("QSTileViewImpl#handleStateChanged") { handleStateChanged(state) }
         }
 
         // We want all instances of this runnable to be equal to each other, so they can be used to
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 3afbd7c..e8623f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -101,7 +101,10 @@
 
     override fun addCallback(callback: QSTile.Callback?) {
         callback ?: return
-        synchronized(callbacks) { callbacks.add(callback) }
+        synchronized(callbacks) {
+            callbacks.add(callback)
+            state?.let(callback::onStateChanged)
+        }
     }
 
     override fun removeCallback(callback: QSTile.Callback?) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index e57a0fd..3f6c58d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.screenrecord
 
+import android.annotation.SuppressLint
 import android.app.Activity
 import android.app.PendingIntent
 import android.content.Intent
@@ -23,6 +24,7 @@
 import android.os.Looper
 import android.os.ResultReceiver
 import android.os.UserHandle
+import android.view.MotionEvent.ACTION_MOVE
 import android.view.View
 import android.view.View.GONE
 import android.view.View.VISIBLE
@@ -102,11 +104,19 @@
 
     @LayoutRes override fun getOptionsViewLayoutId(): Int = R.layout.screen_record_options
 
+    @SuppressLint("ClickableViewAccessibility")
     private fun initRecordOptionsView() {
         audioSwitch = dialog.requireViewById(R.id.screenrecord_audio_switch)
         tapsSwitch = dialog.requireViewById(R.id.screenrecord_taps_switch)
+
+        // Add these listeners so that the switch only responds to movement
+        // within its target region, to meet accessibility requirements
+        audioSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
+        tapsSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
+
         tapsView = dialog.requireViewById(R.id.show_taps)
         updateTapsViewVisibility()
+
         options = dialog.requireViewById(R.id.screen_recording_options)
         val a: ArrayAdapter<*> =
             ScreenRecordingAdapter(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index c925010..67ec03f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1302,6 +1302,7 @@
 
     @Override
     public void updateResources() {
+        Trace.beginSection("NSSLC#updateResources");
         final boolean newSplitShadeEnabled =
                 mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
         final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled;
@@ -1318,6 +1319,7 @@
 
         mSplitShadeFullTransitionDistance =
                 mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance);
+        Trace.endSection();
     }
 
     private void onSplitShadeEnabledChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 4a50897..1b096b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -29,8 +29,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.hardware.input.InputManagerGlobal;
@@ -119,7 +118,6 @@
 
     private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
     private final SparseArray<String> mModifierNames = new SparseArray<>();
-    private final SparseArray<Drawable> mSpecialCharacterDrawables = new SparseArray<>();
     private final SparseArray<Drawable> mModifierDrawables = new SparseArray<>();
     // Ordered list of modifiers that are supported. All values in this array must exist in
     // mModifierNames.
@@ -146,7 +144,7 @@
         } else {
             this.mWindowManager = mContext.getSystemService(WindowManager.class);
         }
-        loadResources(context);
+        loadResources(this.mContext);
         createHardcodedShortcuts();
     }
 
@@ -287,7 +285,7 @@
         mSpecialCharacterNames.put(
                 KeyEvent.KEYCODE_NUM_LOCK, context.getString(R.string.keyboard_key_num_lock));
         mSpecialCharacterNames.put(KeyEvent.KEYCODE_MINUS, "-");
-        mSpecialCharacterNames.put(KeyEvent.KEYCODE_GRAVE, "~");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_GRAVE, "`");
         mSpecialCharacterNames.put(KeyEvent.KEYCODE_EQUALS, "=");
 
         mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_0,
@@ -350,19 +348,6 @@
         mModifierNames.put(KeyEvent.META_SYM_ON, "Sym");
         mModifierNames.put(KeyEvent.META_FUNCTION_ON, "Fn");
 
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_DEL, context.getDrawable(R.drawable.ic_ksh_key_backspace));
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_ENTER, context.getDrawable(R.drawable.ic_ksh_key_enter));
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_DPAD_UP, context.getDrawable(R.drawable.ic_ksh_key_up));
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_DPAD_RIGHT, context.getDrawable(R.drawable.ic_ksh_key_right));
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_DPAD_DOWN, context.getDrawable(R.drawable.ic_ksh_key_down));
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_DPAD_LEFT, context.getDrawable(R.drawable.ic_ksh_key_left));
-
         mModifierDrawables.put(
                 KeyEvent.META_META_ON, context.getDrawable(R.drawable.ic_ksh_key_meta));
     }
@@ -508,7 +493,7 @@
                         Arrays.asList(
                                 Pair.create(KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON))),
                 /* Back: go back to previous state (back button) */
-                /* Meta + ~, Meta + backspace, Meta + left arrow */
+                /* Meta + Grave, Meta + backspace, Meta + left arrow */
                 new ShortcutKeyGroupMultiMappingInfo(
                         context.getString(R.string.group_system_go_back),
                         Arrays.asList(
@@ -826,11 +811,12 @@
                 new BottomSheetDialog(mContext);
         final View keyboardShortcutsView = inflater.inflate(
                 R.layout.keyboard_shortcuts_search_view, null);
+        LinearLayout shortcutsContainer = keyboardShortcutsView.findViewById(
+                R.id.keyboard_shortcuts_container);
         mNoSearchResults = keyboardShortcutsView.findViewById(R.id.shortcut_search_no_result);
         mKeyboardShortcutsBottomSheetDialog.setContentView(keyboardShortcutsView);
         setButtonsDefaultStatus(keyboardShortcutsView);
-        populateKeyboardShortcutSearchList(
-                keyboardShortcutsView.findViewById(R.id.keyboard_shortcuts_container));
+        populateKeyboardShortcutSearchList(shortcutsContainer);
 
         // Workaround for solve issue about dialog not full expanded when landscape.
         FrameLayout bottomSheet = (FrameLayout)
@@ -880,9 +866,14 @@
                     @Override
                     public void afterTextChanged(Editable s) {
                         mQueryString = s.toString();
-                        populateKeyboardShortcutSearchList(
-                                keyboardShortcutsView.findViewById(
-                                        R.id.keyboard_shortcuts_container));
+                        populateKeyboardShortcutSearchList(shortcutsContainer);
+                        if (mNoSearchResults.getVisibility() == View.VISIBLE) {
+                            shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+                                    R.string.keyboard_shortcut_search_list_no_result));
+                        } else if (mSearchEditText.getText().length() > 0) {
+                            shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+                                    R.string.keyboard_shortcut_a11y_show_search_results));
+                        }
                     }
 
                     @Override
@@ -1034,16 +1025,32 @@
                             StringDrawableContainer shortcutRepresentation = shortcutKeys.get(k);
                             if (shortcutRepresentation.mDrawable != null) {
                                 ImageView shortcutKeyIconView = (ImageView) inflater.inflate(
-                                        R.layout.keyboard_shortcuts_key_new_icon_view,
+                                        R.layout.keyboard_shortcuts_key_icon_view,
                                         shortcutItemsContainer,
                                         false);
-                                Bitmap bitmap = Bitmap.createBitmap(shortcutKeyIconItemHeightWidth,
-                                        shortcutKeyIconItemHeightWidth, Bitmap.Config.ARGB_8888);
-                                Canvas canvas = new Canvas(bitmap);
-                                shortcutRepresentation.mDrawable.setBounds(0, 0, canvas.getWidth(),
-                                        canvas.getHeight());
-                                shortcutRepresentation.mDrawable.draw(canvas);
-                                shortcutKeyIconView.setImageBitmap(bitmap);
+                                shortcutKeyIconView.setImageDrawable(
+                                        shortcutRepresentation.mDrawable);
+                                // Once the view has been measured, scale and position the icon in
+                                // the center.
+                                shortcutKeyIconView.post(() -> {
+                                    Drawable d = shortcutKeyIconView.getDrawable();
+
+                                    float newSize = mContext.getResources().getDimensionPixelSize(
+                                            R.dimen.ksh_icon_scaled_size);
+                                    int viewWidth = shortcutKeyIconView.getWidth();
+                                    int viewHeight = shortcutKeyIconView.getHeight();
+                                    float scaleFactor = newSize / d.getIntrinsicWidth();
+                                    // Assumes that top/bottom and left/right padding are equal.
+                                    int paddingHorizontal =  shortcutKeyIconView.getPaddingLeft();
+                                    int paddingVertical =  shortcutKeyIconView.getPaddingTop();
+
+                                    Matrix m = new Matrix();
+                                    m.postScale(scaleFactor, scaleFactor);
+                                    m.postTranslate(
+                                            (viewWidth - newSize) / 2 - paddingHorizontal,
+                                            (viewHeight - newSize) / 2 - paddingVertical);
+                                    shortcutKeyIconView.setImageMatrix(m);
+                                });
                                 shortcutKeyIconView.setImportantForAccessibility(
                                         IMPORTANT_FOR_ACCESSIBILITY_YES);
                                 shortcutKeyIconView.setAccessibilityDelegate(
@@ -1052,7 +1059,7 @@
                                 shortcutItemsContainer.addView(shortcutKeyIconView);
                             } else if (shortcutRepresentation.mString != null) {
                                 TextView shortcutKeyTextView = (TextView) inflater.inflate(
-                                        R.layout.keyboard_shortcuts_key_new_view,
+                                        R.layout.keyboard_shortcuts_key_view,
                                         shortcutItemsContainer,
                                         false);
                                 shortcutKeyTextView.setMinimumWidth(shortcutKeyTextItemMinWidth);
@@ -1062,18 +1069,10 @@
                                                 shortcutRepresentation.mString));
                                 shortcutItemsContainer.addView(shortcutKeyTextView);
                             }
-
-                            if (k < shortcutKeysSize - 1) {
-                                TextView shortcutKeyTextView = (TextView) inflater.inflate(
-                                        R.layout.keyboard_shortcuts_key_plus_view,
-                                        shortcutItemsContainer,
-                                        false);
-                                shortcutItemsContainer.addView(shortcutKeyTextView);
-                            }
                         }
                     } else {
                         TextView shortcutKeyTextView = (TextView) inflater.inflate(
-                                R.layout.keyboard_shortcuts_key_new_view,
+                                R.layout.keyboard_shortcuts_key_view,
                                 shortcutItemsContainer,
                                 false);
                         shortcutKeyTextView.setMinimumWidth(shortcutKeyTextItemMinWidth);
@@ -1085,7 +1084,7 @@
 
                     if (p < keyGroupItemsSize - 1) {
                         TextView shortcutKeyTextView = (TextView) inflater.inflate(
-                                R.layout.keyboard_shortcuts_key_vertical_bar_view,
+                                R.layout.keyboard_shortcuts_key_separator_view,
                                 shortcutItemsContainer,
                                 false);
                         shortcutItemsContainer.addView(shortcutKeyTextView);
@@ -1124,9 +1123,6 @@
         Drawable shortcutKeyDrawable = null;
         if (info.getBaseCharacter() > Character.MIN_VALUE) {
             shortcutKeyString = String.valueOf(info.getBaseCharacter());
-        } else if (mSpecialCharacterDrawables.get(info.getKeycode()) != null) {
-            shortcutKeyDrawable = mSpecialCharacterDrawables.get(info.getKeycode());
-            shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
         } else if (mSpecialCharacterNames.get(info.getKeycode()) != null) {
             shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
         } else {
@@ -1232,28 +1228,35 @@
         mButtonOpenApps = keyboardShortcutsView.findViewById(R.id.shortcut_open_apps);
         mButtonSpecificApp = keyboardShortcutsView.findViewById(R.id.shortcut_specific_app);
 
+        LinearLayout shortcutsContainer = keyboardShortcutsView.findViewById(
+                R.id.keyboard_shortcuts_container);
+
         mButtonSystem.setOnClickListener(v -> {
             setCurrentCategoryIndex(SHORTCUT_SYSTEM_INDEX);
-            populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
-                    R.id.keyboard_shortcuts_container));
+            populateKeyboardShortcutSearchList(shortcutsContainer);
+            shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+                    R.string.keyboard_shortcut_a11y_filter_system));
         });
 
         mButtonInput.setOnClickListener(v -> {
             setCurrentCategoryIndex(SHORTCUT_INPUT_INDEX);
-            populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
-                    R.id.keyboard_shortcuts_container));
+            populateKeyboardShortcutSearchList(shortcutsContainer);
+            shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+                    R.string.keyboard_shortcut_a11y_filter_input));
         });
 
         mButtonOpenApps.setOnClickListener(v -> {
             setCurrentCategoryIndex(SHORTCUT_OPENAPPS_INDEX);
-            populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
-                    R.id.keyboard_shortcuts_container));
+            populateKeyboardShortcutSearchList(shortcutsContainer);
+            shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+                    R.string.keyboard_shortcut_a11y_filter_open_apps));
         });
 
         mButtonSpecificApp.setOnClickListener(v -> {
             setCurrentCategoryIndex(SHORTCUT_SPECIFICAPP_INDEX);
-            populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
-                    R.id.keyboard_shortcuts_container));
+            populateKeyboardShortcutSearchList(shortcutsContainer);
+            shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+                    R.string.keyboard_shortcut_a11y_filter_current_app));
         });
 
         mFullButtonList.add(mButtonSystem);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 61eaff9..acb00d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -85,7 +85,6 @@
 
     private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
     private final SparseArray<String> mModifierNames = new SparseArray<>();
-    private final SparseArray<Drawable> mSpecialCharacterDrawables = new SparseArray<>();
     private final SparseArray<Drawable> mModifierDrawables = new SparseArray<>();
     // Ordered list of modifiers that are supported. All values in this array must exist in
     // mModifierNames.
@@ -340,19 +339,6 @@
         mModifierNames.put(KeyEvent.META_SYM_ON, "Sym");
         mModifierNames.put(KeyEvent.META_FUNCTION_ON, "Fn");
 
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_DEL, context.getDrawable(R.drawable.ic_ksh_key_backspace));
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_ENTER, context.getDrawable(R.drawable.ic_ksh_key_enter));
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_DPAD_UP, context.getDrawable(R.drawable.ic_ksh_key_up));
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_DPAD_RIGHT, context.getDrawable(R.drawable.ic_ksh_key_right));
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_DPAD_DOWN, context.getDrawable(R.drawable.ic_ksh_key_down));
-        mSpecialCharacterDrawables.put(
-                KeyEvent.KEYCODE_DPAD_LEFT, context.getDrawable(R.drawable.ic_ksh_key_left));
-
         mModifierDrawables.put(
                 KeyEvent.META_META_ON, context.getDrawable(R.drawable.ic_ksh_key_meta));
     }
@@ -747,9 +733,6 @@
         Drawable shortcutKeyDrawable = null;
         if (info.getBaseCharacter() > Character.MIN_VALUE) {
             shortcutKeyString = String.valueOf(info.getBaseCharacter());
-        } else if (mSpecialCharacterDrawables.get(info.getKeycode()) != null) {
-            shortcutKeyDrawable = mSpecialCharacterDrawables.get(info.getKeycode());
-            shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
         } else if (mSpecialCharacterNames.get(info.getKeycode()) != null) {
             shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 31893b4..e90ddf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -15,37 +15,48 @@
 
 package com.android.systemui.statusbar.notification.domain.interactor
 
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.notification.collection.render.NotifStats
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
 class ActiveNotificationsInteractor
 @Inject
 constructor(
     private val repository: ActiveNotificationListRepository,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) {
     /** Notifications actively presented to the user in the notification stack, in order. */
     val topLevelRepresentativeNotifications: Flow<List<ActiveNotificationModel>> =
-        repository.activeNotifications.map { store ->
-            store.renderList.map { key ->
-                val entry =
-                    store[key]
-                        ?: error("Could not find notification with key $key in active notif store.")
-                when (entry) {
-                    is ActiveNotificationGroupModel -> entry.summary
-                    is ActiveNotificationModel -> entry
+        repository.activeNotifications
+            .map { store ->
+                store.renderList.map { key ->
+                    val entry =
+                        store[key]
+                            ?: error(
+                                "Could not find notification with key $key in active notif store."
+                            )
+                    when (entry) {
+                        is ActiveNotificationGroupModel -> entry.summary
+                        is ActiveNotificationModel -> entry
+                    }
                 }
             }
-        }
+            .flowOn(backgroundDispatcher)
 
     /** Are any notifications being actively presented in the notification stack? */
     val areAnyNotificationsPresent: Flow<Boolean> =
-        repository.activeNotifications.map { it.renderList.isNotEmpty() }.distinctUntilChanged()
+        repository.activeNotifications
+            .map { it.renderList.isNotEmpty() }
+            .distinctUntilChanged()
+            .flowOn(backgroundDispatcher)
 
     /**
      * The same as [areAnyNotificationsPresent], but without flows, for easy access in synchronous
@@ -59,6 +70,7 @@
         repository.notifStats
             .map { it.hasClearableAlertingNotifs || it.hasClearableSilentNotifs }
             .distinctUntilChanged()
+            .flowOn(backgroundDispatcher)
 
     fun setNotifStats(notifStats: NotifStats) {
         repository.notifStats.value = notifStats
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
index 87b8e55..73341db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
@@ -15,19 +15,24 @@
  */
 package com.android.systemui.statusbar.notification.domain.interactor
 
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
 
 /** Domain logic pertaining to notifications on the keyguard. */
 class NotificationsKeyguardInteractor
 @Inject
 constructor(
     repository: NotificationsKeyguardViewStateRepository,
+    @Background backgroundDispatcher: CoroutineDispatcher,
 ) {
     /** Is a pulse expansion occurring? */
-    val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding
+    val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding.flowOn(backgroundDispatcher)
 
     /** Are notifications fully hidden from view? */
-    val areNotificationsFullyHidden: Flow<Boolean> = repository.areNotificationsFullyHidden
+    val areNotificationsFullyHidden: Flow<Boolean> =
+        repository.areNotificationsFullyHidden.flowOn(backgroundDispatcher)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 0299114..e0eee96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -34,34 +34,38 @@
         viewModel: FooterViewModel,
         clearAllNotifications: View.OnClickListener,
     ): DisposableHandle {
-        // Listen for changes when the view is attached.
+        // Bind the resource IDs
+        footer.setMessageString(viewModel.message.messageId)
+        footer.setMessageIcon(viewModel.message.iconId)
+        footer.setClearAllButtonText(viewModel.clearAllButton.labelId)
+        footer.setClearAllButtonDescription(viewModel.clearAllButton.accessibilityDescriptionId)
+
+        // Bind the click listeners
+        footer.setClearAllButtonClickListener(clearAllNotifications)
+
+        // Listen for visibility changes when the view is attached.
         return footer.repeatWhenAttached {
             lifecycleScope.launch {
-                viewModel.clearAllButton.collect { button ->
-                    if (button.isVisible.isAnimating) {
+                viewModel.clearAllButton.isVisible.collect { isVisible ->
+                    if (isVisible.isAnimating) {
                         footer.setClearAllButtonVisible(
-                            button.isVisible.value,
+                            isVisible.value,
                             /* animate = */ true,
                         ) { _ ->
-                            button.isVisible.stopAnimating()
+                            isVisible.stopAnimating()
                         }
                     } else {
                         footer.setClearAllButtonVisible(
-                            button.isVisible.value,
+                            isVisible.value,
                             /* animate = */ false,
                         )
                     }
-                    footer.setClearAllButtonText(button.labelId)
-                    footer.setClearAllButtonDescription(button.accessibilityDescriptionId)
-                    footer.setClearAllButtonClickListener(clearAllNotifications)
                 }
             }
 
             lifecycleScope.launch {
-                viewModel.message.collect { message ->
-                    footer.setFooterLabelVisible(message.visible)
-                    footer.setMessageString(message.messageId)
-                    footer.setMessageIcon(message.iconId)
+                viewModel.message.isVisible.collect { visible ->
+                    footer.setFooterLabelVisible(visible)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt
index ea5abef..244555a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt
@@ -18,9 +18,10 @@
 
 import android.annotation.StringRes
 import com.android.systemui.util.ui.AnimatedValue
+import kotlinx.coroutines.flow.Flow
 
 data class FooterButtonViewModel(
     @StringRes val labelId: Int,
     @StringRes val accessibilityDescriptionId: Int,
-    val isVisible: AnimatedValue<Boolean>,
+    val isVisible: Flow<AnimatedValue<Boolean>>,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt
index bc912fb..85cd397 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt
@@ -18,10 +18,11 @@
 
 import android.annotation.DrawableRes
 import android.annotation.StringRes
+import kotlinx.coroutines.flow.StateFlow
 
 /** A ViewModel for the string message that can be shown in the footer. */
 data class FooterMessageViewModel(
     @StringRes val messageId: Int,
     @DrawableRes val iconId: Int,
-    val visible: Boolean,
+    val isVisible: StateFlow<Boolean>,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index 721bea1..e6b0abc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -30,9 +30,7 @@
 import dagger.Provides
 import java.util.Optional
 import javax.inject.Provider
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 
 /** ViewModel for [FooterView]. */
@@ -41,36 +39,32 @@
     seenNotificationsInteractor: SeenNotificationsInteractor,
     shadeInteractor: ShadeInteractor,
 ) {
-    val clearAllButton: Flow<FooterButtonViewModel> =
-        activeNotificationsInteractor.hasClearableNotifications
-            .sample(
-                combine(
-                        shadeInteractor.isShadeFullyExpanded,
-                        shadeInteractor.isShadeTouchable,
-                        ::Pair
-                    )
-                    .onStart { emit(Pair(false, false)) }
-            ) { hasClearableNotifications, (isShadeFullyExpanded, animationsEnabled) ->
-                val shouldAnimate = isShadeFullyExpanded && animationsEnabled
-                AnimatableEvent(hasClearableNotifications, shouldAnimate)
-            }
-            .toAnimatedValueFlow()
-            .map { visible ->
-                FooterButtonViewModel(
-                    labelId = R.string.clear_all_notifications_text,
-                    accessibilityDescriptionId = R.string.accessibility_clear_all,
-                    isVisible = visible,
-                )
-            }
+    val clearAllButton: FooterButtonViewModel =
+        FooterButtonViewModel(
+            labelId = R.string.clear_all_notifications_text,
+            accessibilityDescriptionId = R.string.accessibility_clear_all,
+            isVisible =
+                activeNotificationsInteractor.hasClearableNotifications
+                    .sample(
+                        combine(
+                                shadeInteractor.isShadeFullyExpanded,
+                                shadeInteractor.isShadeTouchable,
+                                ::Pair
+                            )
+                            .onStart { emit(Pair(false, false)) }
+                    ) { hasClearableNotifications, (isShadeFullyExpanded, animationsEnabled) ->
+                        val shouldAnimate = isShadeFullyExpanded && animationsEnabled
+                        AnimatableEvent(hasClearableNotifications, shouldAnimate)
+                    }
+                    .toAnimatedValueFlow(),
+        )
 
-    val message: Flow<FooterMessageViewModel> =
-        seenNotificationsInteractor.hasFilteredOutSeenNotifications.map { hasFilteredOutNotifs ->
-            FooterMessageViewModel(
-                messageId = R.string.unlock_to_see_notif_text,
-                iconId = R.drawable.ic_friction_lock_closed,
-                visible = hasFilteredOutNotifs,
-            )
-        }
+    val message: FooterMessageViewModel =
+        FooterMessageViewModel(
+            messageId = R.string.unlock_to_see_notif_text,
+            iconId = R.drawable.ic_friction_lock_closed,
+            isVisible = seenNotificationsInteractor.hasFilteredOutSeenNotifications,
+        )
 }
 
 @Module
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 6944453..3bbdfd1 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
@@ -1275,6 +1275,7 @@
      * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
      */
     private void updateChildren() {
+        Trace.beginSection("NSSL#updateChildren");
         updateScrollStateForAddedChildren();
         mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
                 ? 0
@@ -1285,6 +1286,7 @@
         } else {
             startAnimationToState();
         }
+        Trace.endSection();
     }
 
     private void onPreDrawDuringAnimation() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 4554085..a4e1a9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -24,10 +24,12 @@
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.common.ui.reinflateAndBindLatest
 import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
@@ -40,6 +42,7 @@
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
@@ -48,12 +51,13 @@
 @Inject
 constructor(
     private val viewModel: NotificationListViewModel,
-    private val metricsLogger: MetricsLogger,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val configuration: ConfigurationState,
     private val configurationController: ConfigurationController,
     private val falsingManager: FalsingManager,
     private val iconAreaController: NotificationIconAreaController,
     private val iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
+    private val metricsLogger: MetricsLogger,
     private val shelfIconViewStore: ShelfNotificationIconViewStore,
 ) {
 
@@ -62,14 +66,17 @@
         viewController: NotificationStackScrollLayoutController
     ) {
         bindShelf(view)
-        bindFooter(view)
-        bindEmptyShade(view)
         bindHideList(viewController, viewModel)
 
-        view.repeatWhenAttached {
-            lifecycleScope.launch {
-                viewModel.isImportantForAccessibility.collect { isImportantForAccessibility ->
-                    view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
+        if (FooterViewRefactor.isEnabled) {
+            bindFooter(view)
+            bindEmptyShade(view)
+
+            view.repeatWhenAttached {
+                lifecycleScope.launch {
+                    viewModel.isImportantForAccessibility.collect { isImportantForAccessibility ->
+                        view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
+                    }
                 }
             }
         }
@@ -101,6 +108,7 @@
                     R.layout.status_bar_notification_footer,
                     parentView,
                     attachToRoot = false,
+                    backgroundDispatcher,
                 ) { footerView: FooterView ->
                     traceSection("bind FooterView") {
                         val disposableHandle =
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 674f169..a3d316b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -22,6 +22,7 @@
 import android.hardware.biometrics.BiometricSourceType
 import android.provider.Settings
 import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.ListenersTracing.forEachTraced
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -186,7 +187,9 @@
                 }
         }
 
-    private fun notifyListeners() = listeners.forEach { it.onBypassStateChanged(bypassEnabled) }
+    private fun notifyListeners() = listeners.forEachTraced("KeyguardBypassController") {
+        it.onBypassStateChanged(bypassEnabled)
+    }
 
     /**
      * Notify that the biometric unlock has happened.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
index a32a5ab..8f1ac81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
@@ -22,11 +22,14 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.app.tracing.ListenersTracing;
 import com.android.internal.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.util.Assert;
 
+import kotlin.Unit;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -75,7 +78,11 @@
             mCurrentDevicePosture =
                     mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
 
-            mListeners.forEach(l -> l.onPostureChanged(mCurrentDevicePosture));
+            ListenersTracing.INSTANCE.forEachTraced(mListeners, "DevicePostureControllerImpl",
+                    l -> {
+                        l.onPostureChanged(mCurrentDevicePosture);
+                        return Unit.INSTANCE;
+                    });
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index e4e9554..b598782 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -125,14 +125,19 @@
      * is different.
      */
     public void refreshStatusBarHeight() {
-        int heightFromConfig = SystemBarUtils.getStatusBarHeight(mContext);
+        Trace.beginSection("StatusBarWindowController#refreshStatusBarHeight");
+        try {
+            int heightFromConfig = SystemBarUtils.getStatusBarHeight(mContext);
 
-        if (mBarHeight != heightFromConfig) {
-            mBarHeight = heightFromConfig;
-            apply(mCurrentState);
+            if (mBarHeight != heightFromConfig) {
+                mBarHeight = heightFromConfig;
+                apply(mCurrentState);
+            }
+
+            if (DEBUG) Log.v(TAG, "defineSlots");
+        } finally {
+            Trace.endSection();
         }
-
-        if (DEBUG) Log.v(TAG, "defineSlots");
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 5a9f5d5..886fa70 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -19,6 +19,7 @@
 import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
 
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.Flags.themeOverlayControllerWakefulnessDeprecation;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_HOME;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_LOCK;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET;
@@ -71,12 +72,15 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.monet.ColorScheme;
 import com.android.systemui.monet.Style;
 import com.android.systemui.monet.TonalPalette;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.SecureSettings;
 
 import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
@@ -127,7 +131,6 @@
     private final SecureSettings mSecureSettings;
     private final Executor mMainExecutor;
     private final Handler mBgHandler;
-    private final boolean mIsMonochromaticEnabled;
     private final Context mContext;
     private final boolean mIsMonetEnabled;
     private final boolean mIsFidelityEnabled;
@@ -161,6 +164,8 @@
     private final SparseArray<WallpaperColors> mDeferredWallpaperColors = new SparseArray<>();
     private final SparseIntArray mDeferredWallpaperColorsFlags = new SparseIntArray();
     private final WakefulnessLifecycle mWakefulnessLifecycle;
+    private final JavaAdapter mJavaAdapter;
+    private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     private final UiModeManager mUiModeManager;
     private DynamicScheme mDynamicSchemeDark;
     private DynamicScheme mDynamicSchemeLight;
@@ -200,8 +205,12 @@
                 return;
             }
             boolean currentUser = userId == mUserTracker.getUserId();
-            if (currentUser && !mAcceptColorEvents
-                    && mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP) {
+            boolean isAsleep = themeOverlayControllerWakefulnessDeprecation()
+                    ? mKeyguardTransitionInteractor.isFinishedInStateWhereValue(
+                        state -> KeyguardState.Companion.deviceIsAsleepInState(state))
+                    : mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP;
+
+            if (currentUser && !mAcceptColorEvents && isAsleep) {
                 mDeferredWallpaperColors.put(userId, wallpaperColors);
                 mDeferredWallpaperColorsFlags.put(userId, which);
                 Log.i(TAG, "colors received; processing deferred until screen off: "
@@ -395,9 +404,10 @@
             FeatureFlags featureFlags,
             @Main Resources resources,
             WakefulnessLifecycle wakefulnessLifecycle,
+            JavaAdapter javaAdapter,
+            KeyguardTransitionInteractor keyguardTransitionInteractor,
             UiModeManager uiModeManager) {
         mContext = context;
-        mIsMonochromaticEnabled = featureFlags.isEnabled(Flags.MONOCHROMATIC_THEME);
         mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET);
         mIsFidelityEnabled = featureFlags.isEnabled(Flags.COLOR_FIDELITY);
         mDeviceProvisionedController = deviceProvisionedController;
@@ -412,6 +422,8 @@
         mUserTracker = userTracker;
         mResources = resources;
         mWakefulnessLifecycle = wakefulnessLifecycle;
+        mJavaAdapter = javaAdapter;
+        mKeyguardTransitionInteractor = keyguardTransitionInteractor;
         mUiModeManager = uiModeManager;
         dumpManager.registerDumpable(TAG, this);
     }
@@ -494,21 +506,34 @@
         }
         mWallpaperManager.addOnColorsChangedListener(mOnColorsChangedListener, null,
                 UserHandle.USER_ALL);
-        mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
-            @Override
-            public void onFinishedGoingToSleep() {
-                final int userId = mUserTracker.getUserId();
-                final WallpaperColors colors = mDeferredWallpaperColors.get(userId);
-                if (colors != null) {
-                    int flags = mDeferredWallpaperColorsFlags.get(userId);
 
-                    mDeferredWallpaperColors.put(userId, null);
-                    mDeferredWallpaperColorsFlags.put(userId, 0);
+        Runnable whenAsleepHandler = () -> {
+            final int userId = mUserTracker.getUserId();
+            final WallpaperColors colors = mDeferredWallpaperColors.get(userId);
+            if (colors != null) {
+                int flags = mDeferredWallpaperColorsFlags.get(userId);
 
-                    handleWallpaperColors(colors, flags, userId);
-                }
+                mDeferredWallpaperColors.put(userId, null);
+                mDeferredWallpaperColorsFlags.put(userId, 0);
+
+                handleWallpaperColors(colors, flags, userId);
             }
-        });
+        };
+
+        if (themeOverlayControllerWakefulnessDeprecation()) {
+            mJavaAdapter.alwaysCollectFlow(
+                    mKeyguardTransitionInteractor.isFinishedInState(KeyguardState.DOZING),
+                    isFinishedInDozing -> {
+                        if (isFinishedInDozing) whenAsleepHandler.run();
+                    });
+        } else {
+            mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
+                @Override
+                public void onFinishedGoingToSleep() {
+                    whenAsleepHandler.run();
+                }
+            });
+        }
     }
 
     private void reevaluateSystemTheme(boolean forceReload) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt
index a2a44e4..b2297d0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt
@@ -30,14 +30,16 @@
 
     private val loopedCallback = LoopedCallback()
 
+    private var isLoopedCallbackRegistered: Boolean = false
+
     override fun start() {
         animatable2.start()
-        animatable2.registerAnimationCallback(loopedCallback)
+        setLoopingRegistered(true)
     }
 
     override fun stop() {
         // stop looping if someone stops the animation
-        animatable2.unregisterAnimationCallback(loopedCallback)
+        setLoopingRegistered(false)
         animatable2.stop()
     }
 
@@ -49,7 +51,25 @@
     override fun unregisterAnimationCallback(callback: Animatable2.AnimationCallback): Boolean =
         animatable2.unregisterAnimationCallback(callback)
 
-    override fun clearAnimationCallbacks() = animatable2.clearAnimationCallbacks()
+    override fun clearAnimationCallbacks() {
+        animatable2.clearAnimationCallbacks()
+        // re-register looped callback to maintain looped behaviour. LoopedCallback is a static
+        // class and it has no extra references, so it doesn't provoke a memory leak.
+        isLoopedCallbackRegistered = false
+        setLoopingRegistered(true)
+    }
+
+    private fun setLoopingRegistered(isLooping: Boolean) {
+        if (isLooping == isLoopedCallbackRegistered) {
+            return
+        }
+        isLoopedCallbackRegistered = isLooping
+        if (isLooping) {
+            animatable2.registerAnimationCallback(loopedCallback)
+        } else {
+            animatable2.unregisterAnimationCallback(loopedCallback)
+        }
+    }
 
     override fun getConstantState(): ConstantState? =
         drawable!!.constantState?.let(LoopedAnimatable2DrawableWrapper::LoopedDrawableState)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
index c825d2e..834179bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
@@ -38,6 +38,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -97,7 +98,8 @@
                 deviceStateManager,
                 displayManager,
                 handler,
-                fakeExecutor
+                fakeExecutor,
+                UnconfinedTestDispatcher(),
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt
index 034b802..112cec2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt
@@ -30,6 +30,8 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.cancelAndJoin
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -44,102 +46,112 @@
 
     private val configurationController: ConfigurationController = mock()
     private val layoutInflater = TestLayoutInflater()
+    private val backgroundDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(backgroundDispatcher)
 
     val underTest = ConfigurationState(configurationController, context, layoutInflater)
 
     @Test
-    fun reinflateAndBindLatest_inflatesWithoutEmission() = runTest {
-        var callbackCount = 0
-        backgroundScope.launch {
-            underTest.reinflateAndBindLatest<View>(
-                resource = 0,
-                root = null,
-                attachToRoot = false,
-            ) {
-                callbackCount++
-                null
+    fun reinflateAndBindLatest_inflatesWithoutEmission() =
+        testScope.runTest {
+            var callbackCount = 0
+            backgroundScope.launch {
+                underTest.reinflateAndBindLatest<View>(
+                    resource = 0,
+                    root = null,
+                    attachToRoot = false,
+                    backgroundDispatcher,
+                ) {
+                    callbackCount++
+                    null
+                }
             }
-        }
 
-        // Inflates without an emission
-        runCurrent()
-        assertThat(layoutInflater.inflationCount).isEqualTo(1)
-        assertThat(callbackCount).isEqualTo(1)
-    }
-
-    @Test
-    fun reinflateAndBindLatest_reinflatesOnThemeChanged() = runTest {
-        var callbackCount = 0
-        backgroundScope.launch {
-            underTest.reinflateAndBindLatest<View>(
-                resource = 0,
-                root = null,
-                attachToRoot = false,
-            ) {
-                callbackCount++
-                null
-            }
-        }
-        runCurrent()
-
-        val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
-            verify(configurationController, atLeastOnce()).addCallback(capture())
-        }
-
-        listOf(1, 2, 3).forEach { count ->
-            assertThat(layoutInflater.inflationCount).isEqualTo(count)
-            assertThat(callbackCount).isEqualTo(count)
-            configListeners.forEach { it.onThemeChanged() }
+            // Inflates without an emission
             runCurrent()
+            assertThat(layoutInflater.inflationCount).isEqualTo(1)
+            assertThat(callbackCount).isEqualTo(1)
         }
-    }
 
     @Test
-    fun reinflateAndBindLatest_reinflatesOnDensityOrFontScaleChanged() = runTest {
-        var callbackCount = 0
-        backgroundScope.launch {
-            underTest.reinflateAndBindLatest<View>(
-                resource = 0,
-                root = null,
-                attachToRoot = false,
-            ) {
-                callbackCount++
-                null
+    fun reinflateAndBindLatest_reinflatesOnThemeChanged() =
+        testScope.runTest {
+            var callbackCount = 0
+            backgroundScope.launch {
+                underTest.reinflateAndBindLatest<View>(
+                    resource = 0,
+                    root = null,
+                    attachToRoot = false,
+                    backgroundDispatcher,
+                ) {
+                    callbackCount++
+                    null
+                }
             }
-        }
-        runCurrent()
-
-        val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
-            verify(configurationController, atLeastOnce()).addCallback(capture())
-        }
-
-        listOf(1, 2, 3).forEach { count ->
-            assertThat(layoutInflater.inflationCount).isEqualTo(count)
-            assertThat(callbackCount).isEqualTo(count)
-            configListeners.forEach { it.onDensityOrFontScaleChanged() }
             runCurrent()
-        }
-    }
 
-    @Test
-    fun testReinflateAndBindLatest_disposesOnCancel() = runTest {
-        var callbackCount = 0
-        var disposed = false
-        val job = launch {
-            underTest.reinflateAndBindLatest<View>(
-                resource = 0,
-                root = null,
-                attachToRoot = false,
-            ) {
-                callbackCount++
-                DisposableHandle { disposed = true }
+            val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
+                verify(configurationController, atLeastOnce()).addCallback(capture())
+            }
+
+            listOf(1, 2, 3).forEach { count ->
+                assertThat(layoutInflater.inflationCount).isEqualTo(count)
+                assertThat(callbackCount).isEqualTo(count)
+                configListeners.forEach { it.onThemeChanged() }
+                runCurrent()
             }
         }
 
-        runCurrent()
-        job.cancelAndJoin()
-        assertThat(disposed).isTrue()
-    }
+    @Test
+    fun reinflateAndBindLatest_reinflatesOnDensityOrFontScaleChanged() =
+        testScope.runTest {
+            var callbackCount = 0
+            backgroundScope.launch {
+                underTest.reinflateAndBindLatest<View>(
+                    resource = 0,
+                    root = null,
+                    attachToRoot = false,
+                    backgroundDispatcher,
+                ) {
+                    callbackCount++
+                    null
+                }
+            }
+            runCurrent()
+
+            val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
+                verify(configurationController, atLeastOnce()).addCallback(capture())
+            }
+
+            listOf(1, 2, 3).forEach { count ->
+                assertThat(layoutInflater.inflationCount).isEqualTo(count)
+                assertThat(callbackCount).isEqualTo(count)
+                configListeners.forEach { it.onDensityOrFontScaleChanged() }
+                runCurrent()
+            }
+        }
+
+    @Test
+    fun testReinflateAndBindLatest_disposesOnCancel() =
+        testScope.runTest {
+            var callbackCount = 0
+            var disposed = false
+            val job = launch {
+                underTest.reinflateAndBindLatest<View>(
+                    resource = 0,
+                    root = null,
+                    attachToRoot = false,
+                    backgroundDispatcher,
+                ) {
+                    callbackCount++
+                    DisposableHandle { disposed = true }
+                }
+            }
+
+            runCurrent()
+            job.cancelAndJoin()
+            assertThat(disposed).isTrue()
+        }
 
     inner class TestLayoutInflater : LayoutInflater(context) {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index a58bc52..2b7221e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -34,6 +34,7 @@
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -69,6 +70,7 @@
                 authController,
                 keyguardUpdateMonitor,
                 testScope.backgroundScope,
+                UnconfinedTestDispatcher(),
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
index 9be5558..ae6c5b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
@@ -26,6 +26,7 @@
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -49,7 +50,11 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
         testScope = TestScope()
-        underTest = DevicePostureRepositoryImpl(postureController = devicePostureController)
+        underTest =
+            DevicePostureRepositoryImpl(
+                postureController = devicePostureController,
+                UnconfinedTestDispatcher()
+            )
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 9dfb5a5..e082ca8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -47,13 +47,13 @@
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 
@@ -305,7 +305,11 @@
 
         MediaOutputBaseDialogImpl(Context context, BroadcastSender broadcastSender,
                 MediaOutputController mediaOutputController) {
-            super(context, broadcastSender, mediaOutputController);
+            super(
+                    context,
+                    broadcastSender,
+                    mediaOutputController, /* includePlaybackAndAppMetadata */
+                    true);
 
             mAdapter = mMediaOutputBaseAdapter;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 379136b..d5dc502 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -49,13 +49,13 @@
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 
@@ -394,8 +394,14 @@
 
     @NonNull
     private MediaOutputDialog makeTestDialog(MediaOutputController controller) {
-        return new MediaOutputDialog(mContext, false, mBroadcastSender,
-                controller, mDialogLaunchAnimator, mUiEventLogger);
+        return new MediaOutputDialog(
+                mContext,
+                false,
+                mBroadcastSender,
+                controller,
+                mDialogLaunchAnimator,
+                mUiEventLogger,
+                true);
     }
 
     private void withTestDialog(MediaOutputController controller, Consumer<MediaOutputDialog> c) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index cf43b2e..b7618d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -45,7 +45,6 @@
 import androidx.test.ext.truth.content.IntentSubject.assertThat
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.notetask.NoteTaskController.Companion.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
 import com.android.systemui.notetask.NoteTaskController.Companion.SHORTCUT_ID
@@ -56,6 +55,7 @@
 import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT
 import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
 import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
+import com.android.systemui.res.R
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -162,6 +162,7 @@
             noteTaskBubblesController =
                 FakeNoteTaskBubbleController(context, testDispatcher, Optional.ofNullable(bubbles)),
             applicationScope = testScope,
+            bgCoroutineContext = testScope.backgroundScope.coroutineContext
         )
 
     // region onBubbleExpandChanged
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 1dbb297..62c0ebe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -22,6 +22,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
+
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Looper;
@@ -293,8 +295,10 @@
                 )
         );
 
-        mActiveNotificationsInteractor =
-                new ActiveNotificationsInteractor(new ActiveNotificationListRepository());
+        mActiveNotificationsInteractor = new ActiveNotificationsInteractor(
+                        new ActiveNotificationListRepository(),
+                        StandardTestDispatcher(/* scheduler = */ null, /* name = */ null)
+                );
 
         KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
         keyguardStatusView.setId(R.id.keyguard_status_view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index b86f841..6374d5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -25,14 +25,19 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 
 @SmallTest
 class RenderNotificationsListInteractorTest : SysuiTestCase() {
+    private val backgroundDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(backgroundDispatcher)
 
     private val notifsRepository = ActiveNotificationListRepository()
-    private val notifsInteractor = ActiveNotificationsInteractor(notifsRepository)
+    private val notifsInteractor =
+        ActiveNotificationsInteractor(notifsRepository, backgroundDispatcher)
     private val underTest =
         RenderNotificationListInteractor(
             notifsRepository,
@@ -40,21 +45,26 @@
         )
 
     @Test
-    fun setRenderedList_preservesOrdering() = runTest {
-        val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
-        val keys = (1..50).shuffled().map { "$it" }
-        val entries =
-            keys.map {
-                mock<ListEntry> {
-                    val mockRep = mock<NotificationEntry> {
-                        whenever(key).thenReturn(it)
-                        whenever(sbn).thenReturn(mock())
-                        whenever(icons).thenReturn(mock())
+    fun setRenderedList_preservesOrdering() =
+        testScope.runTest {
+            val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
+            val keys = (1..50).shuffled().map { "$it" }
+            val entries =
+                keys.map {
+                    mock<ListEntry> {
+                        val mockRep =
+                            mock<NotificationEntry> {
+                                whenever(key).thenReturn(it)
+                                whenever(sbn).thenReturn(mock())
+                                whenever(icons).thenReturn(mock())
+                            }
+                        whenever(representativeEntry).thenReturn(mockRep)
                     }
-                    whenever(representativeEntry).thenReturn(mockRep)
                 }
-            }
-        underTest.setRenderedList(entries)
-        assertThat(notifs).comparingElementsUsing(byKey).containsExactlyElementsIn(keys).inOrder()
-    }
+            underTest.setRenderedList(entries)
+            assertThat(notifs)
+                .comparingElementsUsing(byKey)
+                .containsExactlyElementsIn(keys)
+                .inOrder()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 94dcf7a..0ba820f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -116,27 +116,27 @@
     @Test
     fun testMessageVisible_whenFilteredNotifications() =
         testComponent.runTest {
-            val message by collectLastValue(footerViewModel.message)
+            val visible by collectLastValue(footerViewModel.message.isVisible)
 
             activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true
 
-            assertThat(message?.visible).isTrue()
+            assertThat(visible).isTrue()
         }
 
     @Test
     fun testMessageVisible_whenNoFilteredNotifications() =
         testComponent.runTest {
-            val message by collectLastValue(footerViewModel.message)
+            val visible by collectLastValue(footerViewModel.message.isVisible)
 
             activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false
 
-            assertThat(message?.visible).isFalse()
+            assertThat(visible).isFalse()
         }
 
     @Test
     fun testClearAllButtonVisible_whenHasClearableNotifs() =
         testComponent.runTest {
-            val button by collectLastValue(footerViewModel.clearAllButton)
+            val visible by collectLastValue(footerViewModel.clearAllButton.isVisible)
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
@@ -148,13 +148,13 @@
                 )
             runCurrent()
 
-            assertThat(button?.isVisible?.value).isTrue()
+            assertThat(visible?.value).isTrue()
         }
 
     @Test
     fun testClearAllButtonVisible_whenHasNoClearableNotifs() =
         testComponent.runTest {
-            val button by collectLastValue(footerViewModel.clearAllButton)
+            val visible by collectLastValue(footerViewModel.clearAllButton.isVisible)
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
@@ -166,13 +166,13 @@
                 )
             runCurrent()
 
-            assertThat(button?.isVisible?.value).isFalse()
+            assertThat(visible?.value).isFalse()
         }
 
     @Test
     fun testClearAllButtonAnimating_whenShadeExpandedAndTouchable() =
         testComponent.runTest {
-            val button by collectLastValue(footerViewModel.clearAllButton)
+            val visible by collectLastValue(footerViewModel.clearAllButton.isVisible)
             runCurrent()
 
             // WHEN shade is expanded
@@ -200,13 +200,13 @@
             runCurrent()
 
             // THEN button visibility should animate
-            assertThat(button?.isVisible?.isAnimating).isTrue()
+            assertThat(visible?.isAnimating).isTrue()
         }
 
     @Test
     fun testClearAllButtonAnimating_whenShadeNotExpanded() =
         testComponent.runTest {
-            val button by collectLastValue(footerViewModel.clearAllButton)
+            val visible by collectLastValue(footerViewModel.clearAllButton.isVisible)
             runCurrent()
 
             // WHEN shade is collapsed
@@ -234,6 +234,6 @@
             runCurrent()
 
             // THEN button visibility should not animate
-            assertThat(button?.isVisible?.isAnimating).isFalse()
+            assertThat(visible?.isAnimating).isFalse()
         }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index ff5c026..7558974 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.when;
 
 import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
 
 import android.metrics.LogMaker;
 import android.testing.AndroidTestingRunner;
@@ -169,7 +170,8 @@
             new ActiveNotificationListRepository();
 
     private final ActiveNotificationsInteractor mActiveNotificationsInteractor =
-            new ActiveNotificationsInteractor(mActiveNotificationsRepository);
+            new ActiveNotificationsInteractor(mActiveNotificationsRepository,
+                    StandardTestDispatcher(/* scheduler = */ null, /* name = */ null));
 
     private final SeenNotificationsInteractor mSeenNotificationsInteractor =
             new SeenNotificationsInteractor(mActiveNotificationsRepository);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index c454b45..1123688 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -61,10 +61,12 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.monet.Style;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.SecureSettings;
 
 import com.google.common.util.concurrent.MoreExecutors;
@@ -88,7 +90,10 @@
 
     private static final int USER_SYSTEM = UserHandle.USER_SYSTEM;
     private static final int USER_SECONDARY = 10;
-
+    @Mock
+    private JavaAdapter mJavaAdapter;
+    @Mock
+    private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     private ThemeOverlayController mThemeOverlayController;
     @Mock
     private Executor mBgExecutor;
@@ -150,11 +155,12 @@
                 .thenReturn(Color.YELLOW);
         when(mResources.getColor(eq(android.R.color.system_neutral2_500), any()))
                 .thenReturn(Color.BLACK);
+
         mThemeOverlayController = new ThemeOverlayController(mContext,
                 mBroadcastDispatcher, mBgHandler, mMainExecutor, mBgExecutor, mThemeOverlayApplier,
                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
                 mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
-                mUiModeManager) {
+                mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) {
             @VisibleForTesting
             protected boolean isNightMode() {
                 return false;
@@ -736,7 +742,7 @@
                 mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier,
                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
                 mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
-                mUiModeManager) {
+                mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) {
             @VisibleForTesting
             protected boolean isNightMode() {
                 return false;
@@ -776,7 +782,7 @@
                 mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier,
                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
                 mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
-                mUiModeManager) {
+                mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) {
             @VisibleForTesting
             protected boolean isNightMode() {
                 return false;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
index 6d2f00d..080689a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
@@ -59,13 +59,36 @@
     }
 
     @Test
+    fun multipleStartAddsTheCallbackOnce() {
+        underTest.start()
+        underTest.start()
+        underTest.start()
+        underTest.start()
+
+        verify(drawable).registerAnimationCallback(any())
+    }
+
+    @Test
     fun stopRemovesTheCallback() {
+        underTest.start()
+
         underTest.stop()
 
         verify(drawable).unregisterAnimationCallback(any())
     }
 
     @Test
+    fun callbackSurvivesClearAnimationCallbacks() {
+        underTest.start()
+
+        underTest.clearAnimationCallbacks()
+
+        verify(drawable).clearAnimationCallbacks()
+        // start + re-add after #clearAnimationCallbacks
+        verify(drawable, times(2)).registerAnimationCallback(capture(callbackCaptor))
+    }
+
+    @Test
     fun animationLooped() {
         underTest.start()
         verify(drawable).registerAnimationCallback(capture(callbackCaptor))
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 8aa729c..52c25f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -522,7 +522,8 @@
                     (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0;
         });
 
-        mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
+        mPositioner = new TestableBubblePositioner(mContext,
+                mContext.getSystemService(WindowManager.class));
         mPositioner.setMaxBubbles(5);
         mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, mEducationController,
                 syncExecutor);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
index 3d7fb6d..01f4535 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
@@ -17,7 +17,10 @@
 package com.android.systemui.statusbar.notification.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 
 val Kosmos.activeNotificationsInteractor by
-    Kosmos.Fixture { ActiveNotificationsInteractor(activeNotificationListRepository) }
+    Kosmos.Fixture {
+        ActiveNotificationsInteractor(activeNotificationListRepository, testDispatcher)
+    }
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 3f46ab8..e013a3e 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -34,6 +34,7 @@
         "framework-minus-apex.ravenwood",
         "junit",
     ],
+    sdk_version: "core_current",
     visibility: ["//frameworks/base"],
 }
 
diff --git a/services/Android.bp b/services/Android.bp
index 02a7a78..5cb8ec6 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -140,6 +140,7 @@
         ":services.voiceinteraction-sources",
         ":services.wallpapereffectsgeneration-sources",
         ":services.wifi-sources",
+        ":framework-pm-common-shared-srcs",
     ],
     visibility: ["//visibility:private"],
 }
diff --git a/services/companion/java/com/android/server/companion/virtual/OWNERS b/services/companion/java/com/android/server/companion/virtual/OWNERS
index 5295ec8..4fe0592 100644
--- a/services/companion/java/com/android/server/companion/virtual/OWNERS
+++ b/services/companion/java/com/android/server/companion/virtual/OWNERS
@@ -2,7 +2,7 @@
 
 set noparent
 
-ogunwale@google.com
-michaelwr@google.com
+marvinramin@google.com
 vladokom@google.com
-marvinramin@google.com
\ No newline at end of file
+ogunwale@google.com
+michaelwr@google.com
\ No newline at end of file
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 e51ef29..d194bb2 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -187,9 +187,7 @@
             CompanionDeviceManager cdm =
                     getContext().getSystemService(CompanionDeviceManager.class);
             if (cdm != null) {
-                synchronized (mVirtualDeviceManagerLock) {
-                    mActiveAssociations = cdm.getAllAssociations(UserHandle.USER_ALL);
-                }
+                onCdmAssociationsChanged(cdm.getAllAssociations(UserHandle.USER_ALL));
                 cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
                         this::onCdmAssociationsChanged, UserHandle.USER_ALL);
             } else {
@@ -345,19 +343,21 @@
 
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void onCdmAssociationsChanged(List<AssociationInfo> associations) {
+        List<AssociationInfo> vdmAssociations = new ArrayList<>();
+        Set<Integer> activeAssociationIds = new HashSet<>();
+        for (int i = 0; i < associations.size(); ++i) {
+            AssociationInfo association = associations.get(i);
+            if (VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(association.getDeviceProfile())) {
+                vdmAssociations.add(association);
+                activeAssociationIds.add(association.getId());
+            }
+        }
         Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>();
         Set<String> removedPersistentDeviceIds = new HashSet<>();
         synchronized (mVirtualDeviceManagerLock) {
-            Set<Integer> activeAssociationIds = new HashSet<>(associations.size());
-            for (int i = 0; i < associations.size(); ++i) {
-                activeAssociationIds.add(associations.get(i).getId());
-            }
-
             for (int i = 0; i < mActiveAssociations.size(); ++i) {
                 AssociationInfo associationInfo = mActiveAssociations.get(i);
-                if (!activeAssociationIds.contains(associationInfo.getId())
-                        && VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(
-                                associationInfo.getDeviceProfile())) {
+                if (!activeAssociationIds.contains(associationInfo.getId())) {
                     removedPersistentDeviceIds.add(
                             VirtualDeviceImpl.createPersistentDeviceId(associationInfo.getId()));
                 }
@@ -370,7 +370,7 @@
                 }
             }
 
-            mActiveAssociations = associations;
+            mActiveAssociations = vdmAssociations;
         }
 
         for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) {
@@ -869,12 +869,8 @@
             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()));
-                    }
+                    persistentIds.add(
+                            VirtualDeviceImpl.createPersistentDeviceId(associationInfo.getId()));
                 }
             }
             return persistentIds;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c64fb23..54c8ed3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4852,8 +4852,10 @@
         } else {
             Slog.wtf(TAG, "Mismatched or missing ProcessRecord: " + app + ". Pid: " + pid
                     + ". Uid: " + uid);
-            killProcess(pid);
-            killProcessGroup(uid, pid);
+            if (pid > 0) {
+                killProcess(pid);
+                killProcessGroup(uid, pid);
+            }
             mProcessList.noteAppKill(pid, uid,
                     ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
                     ApplicationExitInfo.SUBREASON_UNKNOWN,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9701fc8..d4b72c1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -18,6 +18,8 @@
 
 import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED;
 import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT;
+import static android.media.audio.Flags.autoPublicVolumeApiHardening;
+import static android.media.audio.Flags.focusFreezeTestApi;
 import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET;
 import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER;
 import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
@@ -157,6 +159,7 @@
 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;
@@ -314,6 +317,9 @@
     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;
 
@@ -946,6 +952,8 @@
 
     private final LoudnessCodecHelper mLoudnessCodecHelper;
 
+    private final HardeningEnforcer mHardeningEnforcer;
+
     private final Object mSupportedSystemUsagesLock = new Object();
     @GuardedBy("mSupportedSystemUsagesLock")
     private @AttributeSystemUsage int[] mSupportedSystemUsages =
@@ -1322,6 +1330,8 @@
         mDisplayManager = context.getSystemService(DisplayManager.class);
 
         mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler);
+
+        mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive());
     }
 
     private void initVolumeStreamStates() {
@@ -1393,7 +1403,6 @@
 
         // check on volume initialization
         checkVolumeRangeInitialization("AudioService()");
-
     }
 
     private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener =
@@ -1406,6 +1415,14 @@
                 }
             };
 
+    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
@@ -3434,6 +3451,10 @@
      * 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);
@@ -4214,6 +4235,10 @@
      * 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, true /*canChangeMuteAndUpdateController*/);
     }
@@ -4457,6 +4482,18 @@
                 null /* playbackConfigs */, configs /* recordConfigs */);
     }
 
+    private void dumpFlags(PrintWriter pw) {
+        pw.println("\nFun with Flags: ");
+        pw.println("\tandroid.media.audio.Flags.autoPublicVolumeApiHardening:"
+                + autoPublicVolumeApiHardening());
+        pw.println("\tandroid.media.audio.Flags.focusFreezeTestApi:"
+                + focusFreezeTestApi());
+        pw.println("\tcom.android.media.audio.Flags.bluetoothMacAddressAnonymization:"
+                + bluetoothMacAddressAnonymization());
+        pw.println("\tcom.android.media.audio.Flags.disablePrescaleAbsoluteVolume:"
+                + disablePrescaleAbsoluteVolume());
+    }
+
     private void dumpAudioMode(PrintWriter pw) {
         pw.println("\nAudio mode: ");
         pw.println("- Requested mode = " + AudioSystem.modeToString(getMode()));
@@ -5081,6 +5118,7 @@
     /** @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,
@@ -5450,6 +5488,10 @@
     }
 
     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,6 +6244,35 @@
                 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,
@@ -11405,6 +11476,7 @@
         } else {
             pw.println("\nMessage handler is null");
         }
+        dumpFlags(pw);
         mMediaFocusControl.dump(pw);
         dumpStreamStates(pw);
         dumpVolumeGroups(pw);
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
new file mode 100644
index 0000000..4ceb83b2
--- /dev/null
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -0,0 +1,109 @@
+/*
+ * 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/display/mode/BaseModeRefreshRateVote.java b/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java
new file mode 100644
index 0000000..c04df64
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java
@@ -0,0 +1,59 @@
+/*
+ * 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.mode;
+
+import java.util.Objects;
+
+class BaseModeRefreshRateVote implements Vote {
+
+    /**
+     * The preferred refresh rate selected by the app. It is used to validate that the summary
+     * refresh rate ranges include this value, and are not restricted by a lower priority vote.
+     */
+    final float mAppRequestBaseModeRefreshRate;
+
+    BaseModeRefreshRateVote(float baseModeRefreshRate) {
+        mAppRequestBaseModeRefreshRate = baseModeRefreshRate;
+    }
+
+    @Override
+    public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+        if (summary.appRequestBaseModeRefreshRate == 0f
+                && mAppRequestBaseModeRefreshRate > 0f) {
+            summary.appRequestBaseModeRefreshRate = mAppRequestBaseModeRefreshRate;
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BaseModeRefreshRateVote that)) return false;
+        return Float.compare(that.mAppRequestBaseModeRefreshRate,
+                mAppRequestBaseModeRefreshRate) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAppRequestBaseModeRefreshRate);
+    }
+
+    @Override
+    public String toString() {
+        return "BaseModeRefreshRateVote{ mAppRequestBaseModeRefreshRate="
+                + mAppRequestBaseModeRefreshRate + " }";
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/CombinedVote.java b/services/core/java/com/android/server/display/mode/CombinedVote.java
new file mode 100644
index 0000000..f24fe3a
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/CombinedVote.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.display.mode;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+class CombinedVote implements Vote {
+    final List<Vote> mVotes;
+
+    CombinedVote(List<Vote> votes) {
+        mVotes = Collections.unmodifiableList(votes);
+    }
+
+    @Override
+    public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+        mVotes.forEach(vote -> vote.updateSummary(summary));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CombinedVote that)) return false;
+        return Objects.equals(mVotes, that.mVotes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mVotes);
+    }
+
+    @Override
+    public String toString() {
+        return "CombinedVote{ mVotes=" + mVotes + " }";
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java b/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java
new file mode 100644
index 0000000..2fc5590
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java
@@ -0,0 +1,56 @@
+/*
+ * 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.mode;
+
+import java.util.Objects;
+
+class DisableRefreshRateSwitchingVote implements Vote {
+
+    /**
+     * Whether refresh rate switching should be disabled (i.e. the refresh rate range is
+     * a single value).
+     */
+    final boolean mDisableRefreshRateSwitching;
+
+    DisableRefreshRateSwitchingVote(boolean disableRefreshRateSwitching) {
+        mDisableRefreshRateSwitching = disableRefreshRateSwitching;
+    }
+
+    @Override
+    public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+        summary.disableRefreshRateSwitching =
+                summary.disableRefreshRateSwitching || mDisableRefreshRateSwitching;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof DisableRefreshRateSwitchingVote that)) return false;
+        return mDisableRefreshRateSwitching == that.mDisableRefreshRateSwitching;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDisableRefreshRateSwitching);
+    }
+
+    @Override
+    public String toString() {
+        return "DisableRefreshRateSwitchingVote{ mDisableRefreshRateSwitching="
+                + mDisableRefreshRateSwitching + " }";
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 9587f55..8eb03ec 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -262,7 +262,7 @@
         mVotesStorage.setLoggingEnabled(loggingEnabled);
     }
 
-    private static final class VoteSummary {
+    static final class VoteSummary {
         public float minPhysicalRefreshRate;
         public float maxPhysicalRefreshRate;
         public float minRenderFrameRate;
@@ -274,7 +274,12 @@
         public boolean disableRefreshRateSwitching;
         public float appRequestBaseModeRefreshRate;
 
-        VoteSummary() {
+        public List<SupportedModesVote.SupportedMode> supportedModes;
+
+        final boolean mIsDisplayResolutionRangeVotingEnabled;
+
+        VoteSummary(boolean isDisplayResolutionRangeVotingEnabled) {
+            mIsDisplayResolutionRangeVotingEnabled = isDisplayResolutionRangeVotingEnabled;
             reset();
         }
 
@@ -322,46 +327,7 @@
                 continue;
             }
 
-            // For physical refresh rates, just use the tightest bounds of all the votes.
-            // The refresh rate cannot be lower than the minimal render frame rate.
-            final float minPhysicalRefreshRate = Math.max(vote.refreshRateRanges.physical.min,
-                    vote.refreshRateRanges.render.min);
-            summary.minPhysicalRefreshRate = Math.max(summary.minPhysicalRefreshRate,
-                    minPhysicalRefreshRate);
-            summary.maxPhysicalRefreshRate = Math.min(summary.maxPhysicalRefreshRate,
-                    vote.refreshRateRanges.physical.max);
-
-            // Same goes to render frame rate, but frame rate cannot exceed the max physical
-            // refresh rate
-            final float maxRenderFrameRate = Math.min(vote.refreshRateRanges.render.max,
-                    vote.refreshRateRanges.physical.max);
-            summary.minRenderFrameRate = Math.max(summary.minRenderFrameRate,
-                    vote.refreshRateRanges.render.min);
-            summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, maxRenderFrameRate);
-
-            // For display size, disable refresh rate switching and base mode refresh rate use only
-            // the first vote we come across (i.e. the highest priority vote that includes the
-            // attribute).
-            if (vote.height > 0 && vote.width > 0) {
-                if (summary.width == Vote.INVALID_SIZE && summary.height == Vote.INVALID_SIZE) {
-                    summary.width = vote.width;
-                    summary.height = vote.height;
-                    summary.minWidth = vote.minWidth;
-                    summary.minHeight = vote.minHeight;
-                } else if (mIsDisplayResolutionRangeVotingEnabled) {
-                    summary.width = Math.min(summary.width, vote.width);
-                    summary.height = Math.min(summary.height, vote.height);
-                    summary.minWidth = Math.max(summary.minWidth, vote.minWidth);
-                    summary.minHeight = Math.max(summary.minHeight, vote.minHeight);
-                }
-            }
-            if (!summary.disableRefreshRateSwitching && vote.disableRefreshRateSwitching) {
-                summary.disableRefreshRateSwitching = true;
-            }
-            if (summary.appRequestBaseModeRefreshRate == 0f
-                    && vote.appRequestBaseModeRefreshRate > 0f) {
-                summary.appRequestBaseModeRefreshRate = vote.appRequestBaseModeRefreshRate;
-            }
+            vote.updateSummary(summary);
 
             if (mLoggingEnabled) {
                 Slog.w(TAG, "Vote summary for priority " + Vote.priorityToString(priority)
@@ -443,7 +409,7 @@
 
             ArrayList<Display.Mode> availableModes = new ArrayList<>();
             availableModes.add(defaultMode);
-            VoteSummary primarySummary = new VoteSummary();
+            VoteSummary primarySummary = new VoteSummary(mIsDisplayResolutionRangeVotingEnabled);
             int lowestConsideredPriority = Vote.MIN_PRIORITY;
             int highestConsideredPriority = Vote.MAX_PRIORITY;
 
@@ -526,7 +492,7 @@
                                 + "]");
             }
 
-            VoteSummary appRequestSummary = new VoteSummary();
+            VoteSummary appRequestSummary = new VoteSummary(mIsDisplayResolutionRangeVotingEnabled);
             summarizeVotes(
                     votes,
                     Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF,
diff --git a/services/core/java/com/android/server/display/mode/RefreshRateVote.java b/services/core/java/com/android/server/display/mode/RefreshRateVote.java
new file mode 100644
index 0000000..173b3c5
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/RefreshRateVote.java
@@ -0,0 +1,120 @@
+/*
+ * 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.mode;
+
+import java.util.Objects;
+
+
+/**
+ * Information about the refresh rate frame rate ranges DM would like to set the display to.
+ */
+abstract class RefreshRateVote implements Vote {
+    final float mMinRefreshRate;
+
+    final float mMaxRefreshRate;
+
+    RefreshRateVote(float minRefreshRate, float maxRefreshRate) {
+        mMinRefreshRate = minRefreshRate;
+        mMaxRefreshRate = maxRefreshRate;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof RefreshRateVote that)) return false;
+        return Float.compare(that.mMinRefreshRate, mMinRefreshRate) == 0
+                && Float.compare(that.mMaxRefreshRate, mMaxRefreshRate) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mMinRefreshRate, mMaxRefreshRate);
+    }
+
+    @Override
+    public String toString() {
+        return "RefreshRateVote{  mMinRefreshRate=" + mMinRefreshRate
+                + ", mMaxRefreshRate=" + mMaxRefreshRate + " }";
+    }
+
+    static class RenderVote extends RefreshRateVote {
+        RenderVote(float minRefreshRate, float maxRefreshRate) {
+            super(minRefreshRate, maxRefreshRate);
+        }
+
+        /**
+         * Summary:        minRender            minPhysical                    maxRender
+         *                    v                     v                             v
+         * -------------------|---------------------"-----------------------------|---------
+         *             ^                ^                   ^*             ^           ^
+         *  Vote: min(ignored)     min(applied)  min(applied+physical)  max(applied)  max(ignored)
+         */
+        @Override
+        public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+            summary.minRenderFrameRate = Math.max(summary.minRenderFrameRate, mMinRefreshRate);
+            summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, mMaxRefreshRate);
+            // Physical refresh rate cannot be lower than the minimal render frame rate.
+            summary.minPhysicalRefreshRate = Math.max(summary.minPhysicalRefreshRate,
+                    mMinRefreshRate);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof RefreshRateVote.RenderVote)) return false;
+            return super.equals(o);
+        }
+
+        @Override
+        public String toString() {
+            return "RenderVote{ " + super.toString() + " }";
+        }
+    }
+
+    static class PhysicalVote extends RefreshRateVote {
+        PhysicalVote(float minRefreshRate, float maxRefreshRate) {
+            super(minRefreshRate, maxRefreshRate);
+        }
+
+        /**
+         * Summary:        minPhysical                   maxRender               maxPhysical
+         *                    v                             v                        v
+         * -------------------"-----------------------------|----------------------"----------
+         *             ^             ^             ^*                 ^                   ^
+         *  Vote: min(ignored) min(applied)  max(applied+render)     max(applied)  max(ignored)
+         */
+        @Override
+        public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+            summary.minPhysicalRefreshRate = Math.max(summary.minPhysicalRefreshRate,
+                    mMinRefreshRate);
+            summary.maxPhysicalRefreshRate = Math.min(summary.maxPhysicalRefreshRate,
+                    mMaxRefreshRate);
+            // Render frame rate cannot exceed the max physical refresh rate
+            summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, mMaxRefreshRate);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof RefreshRateVote.PhysicalVote)) return false;
+            return super.equals(o);
+        }
+
+        @Override
+        public String toString() {
+            return "PhysicalVote{ " + super.toString() + " }";
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/SizeVote.java b/services/core/java/com/android/server/display/mode/SizeVote.java
new file mode 100644
index 0000000..a9b18a5
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/SizeVote.java
@@ -0,0 +1,88 @@
+/*
+ * 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.mode;
+
+import java.util.Objects;
+
+class SizeVote implements Vote {
+
+    /**
+     * The requested width of the display in pixels;
+     */
+    final int mWidth;
+
+    /**
+     * The requested height of the display in pixels;
+     */
+    final int mHeight;
+
+    /**
+     * Min requested width of the display in pixels;
+     */
+    final int mMinWidth;
+
+    /**
+     * Min requested height of the display in pixels;
+     */
+    final int mMinHeight;
+
+    SizeVote(int width, int height, int minWidth, int minHeight) {
+        mWidth = width;
+        mHeight = height;
+        mMinWidth = minWidth;
+        mMinHeight = minHeight;
+    }
+
+    @Override
+    public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+        if (mHeight > 0 && mWidth > 0) {
+            // For display size, disable refresh rate switching and base mode refresh rate use
+            // only the first vote we come across (i.e. the highest priority vote that includes
+            // the attribute).
+            if (summary.width == Vote.INVALID_SIZE && summary.height == Vote.INVALID_SIZE) {
+                summary.width = mWidth;
+                summary.height = mHeight;
+                summary.minWidth = mMinWidth;
+                summary.minHeight = mMinHeight;
+            } else if (summary.mIsDisplayResolutionRangeVotingEnabled) {
+                summary.width = Math.min(summary.width, mWidth);
+                summary.height = Math.min(summary.height, mHeight);
+                summary.minWidth = Math.max(summary.minWidth, mMinWidth);
+                summary.minHeight = Math.max(summary.minHeight, mMinHeight);
+            }
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SizeVote sizeVote)) return false;
+        return mWidth == sizeVote.mWidth && mHeight == sizeVote.mHeight
+                && mMinWidth == sizeVote.mMinWidth && mMinHeight == sizeVote.mMinHeight;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mWidth, mHeight, mMinWidth, mMinHeight);
+    }
+
+    @Override
+    public String toString() {
+        return "SizeVote{ mWidth=" + mWidth + ", mHeight=" + mHeight
+                + ", mMinWidth=" + mMinWidth + ", mMinHeight=" + mMinHeight + " }";
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/SupportedModesVote.java b/services/core/java/com/android/server/display/mode/SupportedModesVote.java
new file mode 100644
index 0000000..b31461f
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/SupportedModesVote.java
@@ -0,0 +1,92 @@
+/*
+ * 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.mode;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+class SupportedModesVote implements Vote {
+
+    final List<SupportedMode> mSupportedModes;
+
+    SupportedModesVote(List<SupportedMode> supportedModes) {
+        mSupportedModes = Collections.unmodifiableList(supportedModes);
+    }
+
+    /**
+     * Summary should have subset of supported modes.
+     * If Vote1.supportedModes=(A,B), Vote2.supportedModes=(B,C) then summary.supportedModes=(B)
+     * If summary.supportedModes==null then there is no restriction on supportedModes
+     */
+    @Override
+    public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+        if (summary.supportedModes == null) {
+            summary.supportedModes = new ArrayList<>(mSupportedModes);
+        } else {
+            summary.supportedModes.retainAll(mSupportedModes);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SupportedModesVote that)) return false;
+        return mSupportedModes.equals(that.mSupportedModes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSupportedModes);
+    }
+
+    @Override
+    public String toString() {
+        return "SupportedModesVote{ mSupportedModes=" + mSupportedModes + " }";
+    }
+
+    static class SupportedMode {
+        final float mPeakRefreshRate;
+        final float mVsyncRate;
+
+
+        SupportedMode(float peakRefreshRate, float vsyncRate) {
+            mPeakRefreshRate = peakRefreshRate;
+            mVsyncRate = vsyncRate;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof SupportedMode that)) return false;
+            return Float.compare(that.mPeakRefreshRate, mPeakRefreshRate) == 0
+                    && Float.compare(that.mVsyncRate, mVsyncRate) == 0;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPeakRefreshRate, mVsyncRate);
+        }
+
+        @Override
+        public String toString() {
+            return "SupportedMode{ mPeakRefreshRate=" + mPeakRefreshRate
+                    + ", mVsyncRate=" + mVsyncRate + " }";
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index b6a6069..c1cdd69 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -16,15 +16,13 @@
 
 package com.android.server.display.mode;
 
-import android.view.SurfaceControl;
+import java.util.List;
 
-import java.util.Objects;
-
-final class Vote {
+interface Vote {
     // DEFAULT_RENDER_FRAME_RATE votes for render frame rate [0, DEFAULT]. As the lowest
     // priority vote, it's overridden by all other considerations. It acts to set a default
     // frame rate for a device.
-    static final int PRIORITY_DEFAULT_RENDER_FRAME_RATE = 0;
+    int PRIORITY_DEFAULT_RENDER_FRAME_RATE = 0;
 
     // PRIORITY_FLICKER_REFRESH_RATE votes for a single refresh rate like [60,60], [90,90] or
     // null. It is used to set a preferred refresh rate value in case the higher priority votes
@@ -32,21 +30,21 @@
     static final int PRIORITY_FLICKER_REFRESH_RATE = 1;
 
     // High-brightness-mode may need a specific range of refresh-rates to function properly.
-    static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
+    int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
 
     // SETTING_MIN_RENDER_FRAME_RATE is used to propose a lower bound of the render frame rate.
     // It votes [minRefreshRate, Float.POSITIVE_INFINITY]
-    static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
+    int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
 
     // User setting preferred display resolution.
-    static final int PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE = 4;
+    int PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE = 4;
 
     // APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
     // frame rate in certain cases, mostly to preserve power.
     // @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
     // @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
     // It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
-    static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 5;
+    int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 5;
 
     // We split the app request into different priorities in case we can satisfy one desire
     // without the other.
@@ -72,181 +70,100 @@
     // The preferred refresh rate is set on the main surface of the app outside of
     // DisplayModeDirector.
     // @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
-    static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 6;
+    int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 6;
 
-    static final int PRIORITY_APP_REQUEST_SIZE = 7;
+    int PRIORITY_APP_REQUEST_SIZE = 7;
 
     // SETTING_PEAK_RENDER_FRAME_RATE has a high priority and will restrict the bounds of the
     // rest of low priority voters. It votes [0, max(PEAK, MIN)]
-    static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 8;
+    int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 8;
 
     // Restrict all displays to 60Hz when external display is connected. It votes [59Hz, 61Hz].
-    static final int PRIORITY_SYNCHRONIZED_REFRESH_RATE = 9;
+    int PRIORITY_SYNCHRONIZED_REFRESH_RATE = 9;
 
     // Restrict displays max available resolution and refresh rates. It votes [0, LIMIT]
-    static final int PRIORITY_LIMIT_MODE = 10;
+    int PRIORITY_LIMIT_MODE = 10;
 
     // To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
     // rate to max value (same as for PRIORITY_UDFPS) on lock screen
-    static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 11;
+    int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 11;
 
     // For concurrent displays we want to limit refresh rate on all displays
-    static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 12;
+    int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 12;
 
     // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
     // Settings.Global.LOW_POWER_MODE is on.
-    static final int PRIORITY_LOW_POWER_MODE = 13;
+    int PRIORITY_LOW_POWER_MODE = 13;
 
     // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
     // higher priority voters' result is a range, it will fix the rate to a single choice.
     // It's used to avoid refresh rate switches in certain conditions which may result in the
     // user seeing the display flickering when the switches occur.
-    static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 14;
+    int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 14;
 
     // Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
-    static final int PRIORITY_SKIN_TEMPERATURE = 15;
+    int PRIORITY_SKIN_TEMPERATURE = 15;
 
     // The proximity sensor needs the refresh rate to be locked in order to function, so this is
     // set to a high priority.
-    static final int PRIORITY_PROXIMITY = 16;
+    int PRIORITY_PROXIMITY = 16;
 
     // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
     // to function, so this needs to be the highest priority of all votes.
-    static final int PRIORITY_UDFPS = 17;
+    int PRIORITY_UDFPS = 17;
 
     // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
     // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
 
-    static final int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE;
-    static final int MAX_PRIORITY = PRIORITY_UDFPS;
+    int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE;
+    int MAX_PRIORITY = PRIORITY_UDFPS;
 
     // The cutoff for the app request refresh rate range. Votes with priorities lower than this
     // value will not be considered when constructing the app request refresh rate range.
-    static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
+    int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
             PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE;
 
     /**
      * A value signifying an invalid width or height in a vote.
      */
-    static final int INVALID_SIZE = -1;
+    int INVALID_SIZE = -1;
 
-    /**
-     * The requested width of the display in pixels, or INVALID_SIZE;
-     */
-    public final int width;
-    /**
-     * The requested height of the display in pixels, or INVALID_SIZE;
-     */
-    public final int height;
-    /**
-     * Min requested width of the display in pixels, or 0;
-     */
-    public final int minWidth;
-    /**
-     * Min requested height of the display in pixels, or 0;
-     */
-    public final int minHeight;
-    /**
-     * Information about the refresh rate frame rate ranges DM would like to set the display to.
-     */
-    public final SurfaceControl.RefreshRateRanges refreshRateRanges;
-
-    /**
-     * Whether refresh rate switching should be disabled (i.e. the refresh rate range is
-     * a single value).
-     */
-    public final boolean disableRefreshRateSwitching;
-
-    /**
-     * The preferred refresh rate selected by the app. It is used to validate that the summary
-     * refresh rate ranges include this value, and are not restricted by a lower priority vote.
-     */
-    public final float appRequestBaseModeRefreshRate;
+    void updateSummary(DisplayModeDirector.VoteSummary summary);
 
     static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
-        return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
-                /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
-                /* minPhysicalRefreshRate= */ minRefreshRate,
-                /* maxPhysicalRefreshRate= */ maxRefreshRate,
-                /* minRenderFrameRate= */ 0,
-                /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
-                /* disableRefreshRateSwitching= */ minRefreshRate == maxRefreshRate,
-                /* baseModeRefreshRate= */ 0f);
+        return new CombinedVote(
+                List.of(
+                        new RefreshRateVote.PhysicalVote(minRefreshRate, maxRefreshRate),
+                        new DisableRefreshRateSwitchingVote(minRefreshRate == maxRefreshRate)
+                )
+        );
     }
 
     static Vote forRenderFrameRates(float minFrameRate, float maxFrameRate) {
-        return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
-                /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
-                /* minPhysicalRefreshRate= */ 0,
-                /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
-                minFrameRate,
-                maxFrameRate,
-                /* disableRefreshRateSwitching= */ false,
-                /* baseModeRefreshRate= */ 0f);
+        return new RefreshRateVote.RenderVote(minFrameRate, maxFrameRate);
     }
 
     static Vote forSize(int width, int height) {
-        return new Vote(/* minWidth= */ width, /* minHeight= */ height,
-                width, height,
-                /* minPhysicalRefreshRate= */ 0,
-                /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
-                /* minRenderFrameRate= */ 0,
-                /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
-                /* disableRefreshRateSwitching= */ false,
-                /* baseModeRefreshRate= */ 0f);
+        return new SizeVote(width, height, width, height);
     }
 
     static Vote forSizeAndPhysicalRefreshRatesRange(int minWidth, int minHeight,
             int width, int height, float minRefreshRate, float maxRefreshRate) {
-        return new Vote(minWidth, minHeight,
-                width, height,
-                minRefreshRate,
-                maxRefreshRate,
-                /* minRenderFrameRate= */ 0,
-                /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
-                /* disableRefreshRateSwitching= */ minRefreshRate == maxRefreshRate,
-                /* baseModeRefreshRate= */ 0f);
+        return new CombinedVote(
+                List.of(
+                        new SizeVote(width, height, minWidth, minHeight),
+                        new RefreshRateVote.PhysicalVote(minRefreshRate, maxRefreshRate),
+                        new DisableRefreshRateSwitchingVote(minRefreshRate == maxRefreshRate)
+                )
+        );
     }
 
     static Vote forDisableRefreshRateSwitching() {
-        return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
-                /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
-                /* minPhysicalRefreshRate= */ 0,
-                /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
-                /* minRenderFrameRate= */ 0,
-                /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
-                /* disableRefreshRateSwitching= */ true,
-                /* baseModeRefreshRate= */ 0f);
+        return new DisableRefreshRateSwitchingVote(true);
     }
 
     static Vote forBaseModeRefreshRate(float baseModeRefreshRate) {
-        return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
-                /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
-                /* minPhysicalRefreshRate= */ 0,
-                /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
-                /* minRenderFrameRate= */ 0,
-                /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
-                /* disableRefreshRateSwitching= */ false,
-                /* baseModeRefreshRate= */ baseModeRefreshRate);
-    }
-
-    private Vote(int minWidth, int minHeight,
-            int width, int height,
-            float minPhysicalRefreshRate,
-            float maxPhysicalRefreshRate,
-            float minRenderFrameRate,
-            float maxRenderFrameRate,
-            boolean disableRefreshRateSwitching,
-            float baseModeRefreshRate) {
-        this.minWidth = minWidth;
-        this.minHeight = minHeight;
-        this.width = width;
-        this.height = height;
-        this.refreshRateRanges = new SurfaceControl.RefreshRateRanges(
-                new SurfaceControl.RefreshRateRange(minPhysicalRefreshRate, maxPhysicalRefreshRate),
-                new SurfaceControl.RefreshRateRange(minRenderFrameRate, maxRenderFrameRate));
-        this.disableRefreshRateSwitching = disableRefreshRateSwitching;
-        this.appRequestBaseModeRefreshRate = baseModeRefreshRate;
+        return new BaseModeRefreshRateVote(baseModeRefreshRate);
     }
 
     static String priorityToString(int priority) {
@@ -291,33 +208,4 @@
                 return Integer.toString(priority);
         }
     }
-
-    @Override
-    public String toString() {
-        return "Vote: {"
-                + "minWidth: " + minWidth + ", minHeight: " + minHeight
-                + ", width: " + width + ", height: " + height
-                + ", refreshRateRanges: " + refreshRateRanges
-                + ", disableRefreshRateSwitching: " + disableRefreshRateSwitching
-                + ", appRequestBaseModeRefreshRate: "  + appRequestBaseModeRefreshRate + "}";
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(minWidth, minHeight, width, height, refreshRateRanges,
-                disableRefreshRateSwitching, appRequestBaseModeRefreshRate);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof Vote)) return false;
-        final var vote = (Vote) o;
-        return  minWidth == vote.minWidth && minHeight == vote.minHeight
-                && width == vote.width && height == vote.height
-                && disableRefreshRateSwitching == vote.disableRefreshRateSwitching
-                && Float.compare(vote.appRequestBaseModeRefreshRate,
-                        appRequestBaseModeRefreshRate) == 0
-                && refreshRateRanges.equals(vote.refreshRateRanges);
-    }
 }
diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java
index 49c587a..95fb8fc 100644
--- a/services/core/java/com/android/server/display/mode/VotesStorage.java
+++ b/services/core/java/com/android/server/display/mode/VotesStorage.java
@@ -157,13 +157,19 @@
         }
     }
 
-    private int getMaxPhysicalRefreshRate(@Nullable Vote vote) {
+    private static int getMaxPhysicalRefreshRate(@Nullable Vote vote) {
         if (vote == null) {
             return -1;
-        } else if (vote.refreshRateRanges.physical.max == Float.POSITIVE_INFINITY) {
-            return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable
+        } else if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) {
+            return (int) physicalVote.mMaxRefreshRate;
+        } else if (vote instanceof CombinedVote combinedVote) {
+            return combinedVote.mVotes.stream()
+                    .filter(v -> v instanceof RefreshRateVote.PhysicalVote)
+                    .map(pv -> (int) (((RefreshRateVote.PhysicalVote) pv).mMaxRefreshRate))
+                    .min(Integer::compare)
+                    .orElse(1000); // for visualisation
         }
-        return (int) vote.refreshRateRanges.physical.max;
+        return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable
     }
 
     interface Listener {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 6a43697..4821fbe 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -2486,6 +2486,13 @@
         private void onRequestFailedOnHandler(@NonNull MediaRoute2Provider provider,
                 long uniqueRequestId, int reason) {
             if (handleSessionCreationRequestFailed(provider, uniqueRequestId, reason)) {
+                Slog.w(
+                        TAG,
+                        TextUtils.formatSimple(
+                                "onRequestFailedOnHandler | Finished handling session creation"
+                                    + " request failed for provider: %s, uniqueRequestId: %d,"
+                                    + " reason: %d",
+                                provider.getUniqueId(), uniqueRequestId, reason));
                 return;
             }
 
@@ -2515,6 +2522,12 @@
 
             if (matchingRequest == null) {
                 // The failure is not about creating a session.
+                Slog.w(
+                        TAG,
+                        TextUtils.formatSimple(
+                                "handleSessionCreationRequestFailed | No matching request found for"
+                                    + " provider: %s, uniqueRequestId: %d, reason: %d",
+                                provider.getUniqueId(), uniqueRequestId, reason));
                 return false;
             }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1b640fc..aa0b9b8 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1765,8 +1765,7 @@
             if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
                 // update system notification channels
                 SystemNotificationChannels.createAll(context);
-                mZenModeHelper.updateDefaultZenRules(Binder.getCallingUid(),
-                        isCallerIsSystemOrSystemUi());
+                mZenModeHelper.updateDefaultZenRules(Binder.getCallingUid());
                 mPreferencesHelper.onLocaleChanged(context, ActivityManager.getCurrentUser());
             }
         }
@@ -3039,7 +3038,7 @@
                 mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
 
         mPreferencesHelper.updateNotificationChannel(pkg, uid, channel, true,
-                Binder.getCallingUid(), isCallerIsSystemOrSystemUi());
+                Binder.getCallingUid(), isCallerSystemOrSystemUi());
         if (mPreferencesHelper.onlyHasDefaultChannel(pkg, uid)) {
             mPermissionHelper.setNotificationPermission(pkg, UserHandle.getUserId(uid),
                     channel.getImportance() != IMPORTANCE_NONE, true);
@@ -3087,7 +3086,7 @@
         final NotificationChannelGroup preUpdate =
                 mPreferencesHelper.getNotificationChannelGroup(group.getId(), pkg, uid);
         mPreferencesHelper.createNotificationChannelGroup(pkg, uid, group,
-                fromApp, Binder.getCallingUid(), isCallerIsSystemOrSystemUi());
+                fromApp, Binder.getCallingUid(), isCallerSystemOrSystemUi());
         if (!fromApp) {
             maybeNotifyChannelGroupOwner(pkg, uid, preUpdate, group);
         }
@@ -3517,7 +3516,7 @@
             }
 
             checkCallerIsSameApp(pkg);
-            final boolean isSystemToast = isCallerIsSystemOrSystemUi()
+            final boolean isSystemToast = isCallerSystemOrSystemUi()
                     || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg);
             boolean isAppRenderedToast = (callback != null);
             if (!checkCanEnqueueToast(pkg, callingUid, displayId, isAppRenderedToast,
@@ -4084,7 +4083,7 @@
                         channel, true /* fromTargetApp */,
                         mConditionProviders.isPackageOrComponentAllowed(
                                 pkg, UserHandle.getUserId(uid)), Binder.getCallingUid(),
-                        isCallerIsSystemOrSystemUi());
+                        isCallerSystemOrSystemUi());
                 if (needsPolicyFileChange) {
                     mListeners.notifyNotificationChannelChanged(pkg,
                             UserHandle.getUserHandleForUid(uid),
@@ -4165,7 +4164,7 @@
                 String targetPkg, String channelId, boolean returnParentIfNoConversationChannel,
                 String conversationId) {
             if (canNotifyAsPackage(callingPkg, targetPkg, userId)
-                    || isCallerIsSystemOrSysemUiOrShell()) {
+                    || isCallerSystemOrSystemUiOrShell()) {
                 int targetUid = -1;
                 try {
                     targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
@@ -4220,7 +4219,7 @@
         public void deleteNotificationChannel(String pkg, String channelId) {
             checkCallerIsSystemOrSameApp(pkg);
             final int callingUid = Binder.getCallingUid();
-            final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+            final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
             final int callingUser = UserHandle.getUserId(callingUid);
             if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
                 throw new IllegalArgumentException("Cannot delete default channel");
@@ -4264,7 +4263,7 @@
             checkCallerIsSystemOrSameApp(pkg);
 
             final int callingUid = Binder.getCallingUid();
-            final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+            final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
             NotificationChannelGroup groupToDelete =
                     mPreferencesHelper.getNotificationChannelGroupWithChannels(
                             pkg, callingUid, groupId, false);
@@ -5207,7 +5206,7 @@
         public void requestInterruptionFilterFromListener(INotificationListener token,
                 int interruptionFilter) throws RemoteException {
             final int callingUid = Binder.getCallingUid();
-            final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+            final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationLock) {
@@ -5254,7 +5253,7 @@
         public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
             enforceSystemOrSystemUI("INotificationManager.setZenMode");
             final int callingUid = Binder.getCallingUid();
-            final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+            final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
             final long identity = Binder.clearCallingIdentity();
             try {
                 mZenModeHelper.setManualZenMode(mode, conditionId, null, reason, callingUid,
@@ -5316,7 +5315,9 @@
 
             return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
                     "addAutomaticZenRule", Binder.getCallingUid(),
-                    isCallerIsSystemOrSystemUi());
+                    // TODO: b/308670715: Distinguish FROM_APP from FROM_USER
+                    isCallerSystemOrSystemUi() ? ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI
+                            : ZenModeHelper.FROM_APP);
         }
 
         @Override
@@ -5334,7 +5335,9 @@
 
             return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
                     "updateAutomaticZenRule", Binder.getCallingUid(),
-                    isCallerIsSystemOrSystemUi());
+                    // TODO: b/308670715: Distinguish FROM_APP from FROM_USER
+                    isCallerSystemOrSystemUi() ? ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI
+                            : ZenModeHelper.FROM_APP);
         }
 
         @Override
@@ -5344,7 +5347,7 @@
             enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
 
             return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule",
-                    Binder.getCallingUid(), isCallerIsSystemOrSystemUi());
+                    Binder.getCallingUid(), isCallerSystemOrSystemUi());
         }
 
         @Override
@@ -5354,7 +5357,7 @@
 
             return mZenModeHelper.removeAutomaticZenRules(packageName,
                     packageName + "|removeAutomaticZenRules", Binder.getCallingUid(),
-                    isCallerIsSystemOrSystemUi());
+                    isCallerSystemOrSystemUi());
         }
 
         @Override
@@ -5373,7 +5376,7 @@
             enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
 
             mZenModeHelper.setAutomaticZenRuleState(id, condition, Binder.getCallingUid(),
-                    isCallerIsSystemOrSystemUi());
+                    isCallerSystemOrSystemUi());
         }
 
         @Override
@@ -5382,7 +5385,7 @@
             final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
             if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
             final int callingUid = Binder.getCallingUid();
-            final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+            final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
 
             if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
                 mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, callingUid, zen);
@@ -5477,7 +5480,7 @@
                     () -> CompatChanges.isChangeEnabled(MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES,
                             callingUid));
             return !isCompatChangeEnabled
-                    || isCallerIsSystemOrSystemUi()
+                    || isCallerSystemOrSystemUi()
                     || hasCompanionDevice(callingPkg, UserHandle.getUserId(callingUid),
                             AssociationRequest.DEVICE_PROFILE_WATCH);
         }
@@ -5708,7 +5711,7 @@
         public void setNotificationPolicy(String pkg, Policy policy) {
             enforcePolicyAccess(pkg, "setNotificationPolicy");
             int callingUid = Binder.getCallingUid();
-            boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+            boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
 
             boolean shouldApplyAsImplicitRule = android.app.Flags.modesApi()
                     && !canManageGlobalZenPolicy(pkg, callingUid);
@@ -7110,7 +7113,7 @@
                 }
                 mPreferencesHelper.updateNotificationChannel(
                         pkg, notificationUid, channel, false, callingUid,
-                        isCallerIsSystemOrSystemUi());
+                        isCallerSystemOrSystemUi());
                 r.updateNotificationChannel(channel);
             } else if (!channel.isUserVisibleTaskShown() && !TextUtils.isEmpty(channelId)
                     && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
@@ -10430,7 +10433,7 @@
     }
 
     @VisibleForTesting
-    protected boolean isCallerIsSystemOrSystemUi() {
+    protected boolean isCallerSystemOrSystemUi() {
         if (isCallerSystemOrPhone()) {
             return true;
         }
@@ -10438,12 +10441,12 @@
                 == PERMISSION_GRANTED;
     }
 
-    private boolean isCallerIsSystemOrSysemUiOrShell() {
+    private boolean isCallerSystemOrSystemUiOrShell() {
         int callingUid = Binder.getCallingUid();
         if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
             return true;
         }
-        return isCallerIsSystemOrSystemUi();
+        return isCallerSystemOrSystemUi();
     }
 
     private void checkCallerIsSystemOrShell() {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 2ef0ca6..89d8200 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -27,6 +27,7 @@
 
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
@@ -73,6 +74,7 @@
 import android.provider.Settings.Global;
 import android.service.notification.Condition;
 import android.service.notification.ConditionProviderService;
+import android.service.notification.ZenDeviceEffects;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ZenRule;
 import android.service.notification.ZenModeProto;
@@ -105,6 +107,8 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -129,6 +133,21 @@
     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
     static final long SEND_ACTIVATION_AZR_STATUSES = 308673617L;
 
+    /** A rule addition or update that is initiated by the System or SystemUI. */
+    static final int FROM_SYSTEM_OR_SYSTEMUI = 1;
+    /** A rule addition or update that is initiated by the user (through system settings). */
+    static final int FROM_USER = 2;
+    /** A rule addition or update that is initiated by an app (via NotificationManager APIs). */
+    static final int FROM_APP = 3;
+
+    @IntDef(prefix = { "FROM_" }, value = {
+            FROM_SYSTEM_OR_SYSTEMUI,
+            FROM_USER,
+            FROM_APP
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ChangeOrigin {}
+
     // pkg|userId => uid
     @VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
 
@@ -378,7 +397,7 @@
     }
 
     public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
-            String reason, int callingUid, boolean fromSystemOrSystemUi) {
+            String reason, int callingUid, @ChangeOrigin int origin) {
         if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
             PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
             if (component == null) {
@@ -412,10 +431,10 @@
             }
             newConfig = mConfig.copy();
             ZenRule rule = new ZenRule();
-            populateZenRule(pkg, automaticZenRule, rule, true);
+            populateZenRule(pkg, automaticZenRule, rule, true, origin);
             newConfig.automaticRules.put(rule.id, rule);
             if (setConfigLocked(newConfig, reason, rule.component, true, callingUid,
-                    fromSystemOrSystemUi)) {
+                    origin == FROM_SYSTEM_OR_SYSTEMUI)) {
                 return rule.id;
             } else {
                 throw new AndroidRuntimeException("Could not create rule");
@@ -424,7 +443,7 @@
     }
 
     public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
-            String reason, int callingUid, boolean fromSystemOrSystemUi) {
+            String reason, int callingUid, @ChangeOrigin int origin) {
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
             if (mConfig == null) return false;
@@ -452,9 +471,9 @@
                 }
             }
 
-            populateZenRule(rule.pkg, automaticZenRule, rule, false);
+            populateZenRule(rule.pkg, automaticZenRule, rule, false, origin);
             return setConfigLocked(newConfig, reason, rule.component, true, callingUid,
-                    fromSystemOrSystemUi);
+                    origin == FROM_SYSTEM_OR_SYSTEMUI);
         }
     }
 
@@ -790,7 +809,7 @@
         }
     }
 
-    protected void updateDefaultZenRules(int callingUid, boolean fromSystemOrSystemUi) {
+    protected void updateDefaultZenRules(int callingUid) {
         updateDefaultAutomaticRuleNames();
         synchronized (mConfigLock) {
             for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
@@ -807,7 +826,7 @@
                         // update default rule (if locale changed, name of rule will change)
                         currRule.name = defaultRule.name;
                         updateAutomaticZenRule(defaultRule.id, zenRuleToAutomaticZenRule(currRule),
-                                "locale changed", callingUid, fromSystemOrSystemUi);
+                                "locale changed", callingUid, FROM_SYSTEM_OR_SYSTEMUI);
                     }
                 }
             }
@@ -850,7 +869,11 @@
     }
 
     private static void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
-            boolean isNew) {
+            boolean isNew, @ChangeOrigin int origin) {
+        // TODO: b/308671593,b/311406021 - Handle origins more precisely:
+        //  - FROM_USER can override anything and updates bitmask of user-modified fields;
+        //  - FROM_SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask;
+        //  - FROM_APP can only update if not user-modified.
         if (rule.enabled != automaticZenRule.isEnabled()) {
             rule.snoozing = false;
         }
@@ -861,7 +884,10 @@
         rule.modified = automaticZenRule.isModified();
         rule.zenPolicy = automaticZenRule.getZenPolicy();
         if (Flags.modesApi()) {
-            rule.zenDeviceEffects = automaticZenRule.getDeviceEffects();
+            rule.zenDeviceEffects = fixZenDeviceEffects(
+                    rule.zenDeviceEffects,
+                    automaticZenRule.getDeviceEffects(),
+                    origin);
         }
         rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
                 automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
@@ -882,6 +908,50 @@
         }
     }
 
+    /** "
+     * Fix" {@link ZenDeviceEffects} that are being stored as part of a new or updated ZenRule.
+     *
+     * <ul>
+     *     <li> Apps cannot turn on hidden effects (those tagged as {@code @hide}) since they are
+     *     intended for platform-specific rules (e.g. wearables). If it's a new rule, we blank them
+     *     out; if it's an update, we preserve the previous values.
+     * </ul>
+     */
+    @Nullable
+    private static ZenDeviceEffects fixZenDeviceEffects(@Nullable ZenDeviceEffects oldEffects,
+            @Nullable ZenDeviceEffects newEffects, @ChangeOrigin int origin) {
+        // TODO: b/308671593,b/311406021 - Handle origins more precisely:
+        //  - FROM_USER can override anything and updates bitmask of user-modified fields;
+        //  - FROM_SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask;
+        //  - FROM_APP can only update if not user-modified.
+        if (origin == FROM_SYSTEM_OR_SYSTEMUI || origin == FROM_USER) {
+            return newEffects;
+        }
+
+        if (newEffects == null) {
+            return null;
+        }
+        if (oldEffects != null) {
+            return new ZenDeviceEffects.Builder(newEffects)
+                    .setShouldDisableAutoBrightness(oldEffects.shouldDisableAutoBrightness())
+                    .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake())
+                    .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake())
+                    .setShouldDisableTouch(oldEffects.shouldDisableTouch())
+                    .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage())
+                    .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze())
+                    .build();
+        } else {
+            return new ZenDeviceEffects.Builder(newEffects)
+                    .setShouldDisableAutoBrightness(false)
+                    .setShouldDisableTapToWake(false)
+                    .setShouldDisableTiltToWake(false)
+                    .setShouldDisableTouch(false)
+                    .setShouldMinimizeRadioUsage(false)
+                    .setShouldMaximizeDoze(false)
+                    .build();
+        }
+    }
+
     private static AutomaticZenRule zenRuleToAutomaticZenRule(ZenRule rule) {
         AutomaticZenRule azr;
         if (Flags.modesApi()) {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 3caff11..448f215 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -154,6 +154,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.F2fsUtils;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
@@ -175,7 +176,6 @@
 import com.android.server.pm.parsing.PackageCacher;
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.permission.Permission;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index fc83120..0a81b2b 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -50,9 +50,9 @@
 import android.util.ExceptionUtils;
 import android.util.Slog;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.art.model.DexoptResult;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
index 6faf68d..c66a9e9 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelper.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -22,7 +22,7 @@
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e557f09..233bf4f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -181,6 +181,8 @@
 import com.android.internal.content.F2fsUtils;
 import com.android.internal.content.InstallLocationUtils;
 import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
 import com.android.internal.pm.pkg.component.ParsedMainComponent;
 import com.android.internal.telephony.CarrierAppUtils;
@@ -220,9 +222,7 @@
 import com.android.server.pm.local.PackageManagerLocalImpl;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
 import com.android.server.pm.permission.LegacyPermissionManagerService;
 import com.android.server.pm.permission.LegacyPermissionSettings;
@@ -5216,6 +5216,18 @@
         }
 
         @Override
+        public String getSuspendingPackage(String packageName, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final Computer snapshot = snapshot();
+            // This will do visibility checks as well.
+            if (!snapshot.isPackageSuspendedForUser(packageName, userId)) {
+                return null;
+            }
+            return mSuspendPackageHelper.getSuspendingPackage(snapshot, packageName, userId,
+                    callingUid);
+        }
+
+        @Override
         public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() {
             // allow instant applications
             ArrayList<FeatureInfo> res;
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 434a62d..15d2fdc 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -41,10 +41,10 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.InstallLocationUtils;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.rollback.RollbackManagerInternal;
 
 import java.io.File;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index b50d0a0..293b873 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -44,9 +44,9 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DataClass;
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.permission.LegacyPermissionDataProvider;
 import com.android.server.pm.permission.LegacyPermissionState;
@@ -1732,7 +1732,7 @@
             time = 1700251133016L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
-            inputSignatures = "private  int mBooleans\nprivate  int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate  long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate  int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic  com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic  com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic  com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  boolean isRequestLegacyExternalStorage()\npublic  boolean isUserDataFragile()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n  int[] queryInstalledUsers(int[],boolean)\n  int[] queryUsersInstalledOrHasData(int[])\n  long getCeDataInode(int)\n  long getDeDataInode(int)\n  void setCeDataInode(long,int)\n  void setDeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\npublic  com.android.server.pm.PackageSetting setScannedAsStoppedSystemApp(boolean)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static  void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isIncremental()\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic  com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic  com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic  com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\npublic @java.lang.Override boolean isScannedAsStoppedSystemApp()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final  int INSTALL_PERMISSION_FIXED\nprivate static final  int UPDATE_AVAILABLE\nprivate static final  int FORCE_QUERYABLE_OVERRIDE\nprivate static final  int SCANNED_AS_STOPPED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+            inputSignatures = "private  int mBooleans\nprivate  int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.internal.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate  long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate  int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic  com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic  com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic  com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  boolean isRequestLegacyExternalStorage()\npublic  boolean isUserDataFragile()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n  int[] queryInstalledUsers(int[],boolean)\n  int[] queryUsersInstalledOrHasData(int[])\n  long getCeDataInode(int)\n  long getDeDataInode(int)\n  void setCeDataInode(long,int)\n  void setDeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\npublic  com.android.server.pm.PackageSetting setScannedAsStoppedSystemApp(boolean)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static  void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isIncremental()\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic  com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic  com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic  com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\npublic @java.lang.Override boolean isScannedAsStoppedSystemApp()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final  int INSTALL_PERMISSION_FIXED\nprivate static final  int UPDATE_AVAILABLE\nprivate static final  int FORCE_QUERYABLE_OVERRIDE\nprivate static final  int SCANNED_AS_STOPPED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 5625884..1089ac9 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -22,9 +22,9 @@
 import android.os.Trace;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 
 import java.io.File;
 import java.util.concurrent.ArrayBlockingQueue;
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 5312ae6..bb0017c 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -30,7 +30,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.utils.WatchedLongSparseArray;
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 22ee963..53b84e6 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -73,6 +73,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedMainComponent;
 import com.android.internal.pm.pkg.component.ParsedProcess;
@@ -83,7 +84,6 @@
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateUtils;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
diff --git a/services/core/java/com/android/server/pm/ScanRequest.java b/services/core/java/com/android/server/pm/ScanRequest.java
index e66a72f..37cf30b 100644
--- a/services/core/java/com/android/server/pm/ScanRequest.java
+++ b/services/core/java/com/android/server/pm/ScanRequest.java
@@ -21,7 +21,7 @@
 import android.os.UserHandle;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 585e2e4..d0fdfa9 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -46,11 +46,11 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.SystemConfig;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.utils.Snappable;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8556317..f90bf4b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1957,6 +1957,19 @@
         return userTypeDetails.getStatusBarIcon();
     }
 
+    @Override
+    public @StringRes int getProfileLabelResId(@UserIdInt int userId) {
+        checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+                "getProfileLabelResId");
+        final UserInfo userInfo = getUserInfoNoChecks(userId);
+        final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
+        if (userInfo == null || userTypeDetails == null) {
+            return Resources.ID_NULL;
+        }
+        final int userIndex = userInfo.profileBadge;
+        return userTypeDetails.getLabel(userIndex);
+    }
+
     public boolean isProfile(@UserIdInt int userId) {
         checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
         return isProfileUnchecked(userId);
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 7bdcd68..56c400a0 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -54,8 +54,15 @@
     /** Whether users of this type can be created. */
     private final boolean mEnabled;
 
-    // TODO(b/142482943): Currently unused and not set. Hook this up.
-    private final int mLabel;
+    /**
+     * Resource IDs ({@link StringRes}) of the user's labels. This might be used to label a
+     * user/profile in tabbed views, etc.
+     * The values are resource IDs referring to the strings not the strings themselves.
+     *
+     * <p>This is an array because, in general, there may be multiple users of the same user type.
+     * In this case, the user is indexed according to its {@link UserInfo#profileBadge}.
+     */
+    private final @Nullable int[] mLabels;
 
     /**
      * Maximum number of this user type allowed on the device.
@@ -160,8 +167,8 @@
     private final @NonNull UserProperties mDefaultUserProperties;
 
     private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
-            @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
-            int maxAllowedPerParent,
+            @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags,
+            @Nullable int[] labels, int maxAllowedPerParent,
             int iconBadge, int badgePlain, int badgeNoBackground,
             int statusBarIcon,
             @Nullable int[] badgeLabels, @Nullable int[] badgeColors,
@@ -181,12 +188,11 @@
         this.mDefaultSystemSettings = defaultSystemSettings;
         this.mDefaultSecureSettings = defaultSecureSettings;
         this.mDefaultCrossProfileIntentFilters = defaultCrossProfileIntentFilters;
-
         this.mIconBadge = iconBadge;
         this.mBadgePlain = badgePlain;
         this.mBadgeNoBackground = badgeNoBackground;
         this.mStatusBarIcon = statusBarIcon;
-        this.mLabel = label;
+        this.mLabels = labels;
         this.mBadgeLabels = badgeLabels;
         this.mBadgeColors = badgeColors;
         this.mDarkThemeBadgeColors = darkThemeBadgeColors;
@@ -234,9 +240,16 @@
         return mDefaultUserInfoPropertyFlags | mBaseType;
     }
 
-    // TODO(b/142482943) Hook this up; it is currently unused.
-    public int getLabel() {
-        return mLabel;
+    /**
+     * Returns the resource ID corresponding to the badgeIndexth label name where the badgeIndex is
+     * expected to be the {@link UserInfo#profileBadge} of the user. If badgeIndex exceeds the
+     * number of labels, returns the label for the highest index.
+     */
+    public @StringRes int getLabel(int badgeIndex) {
+        if (mLabels == null || mLabels.length == 0 || badgeIndex < 0) {
+            return Resources.ID_NULL;
+        }
+        return mLabels[Math.min(badgeIndex, mLabels.length - 1)];
     }
 
     /** Returns whether users of this user type should be badged. */
@@ -358,7 +371,6 @@
         pw.print(prefix); pw.print("mMaxAllowedPerParent: "); pw.println(mMaxAllowedPerParent);
         pw.print(prefix); pw.print("mDefaultUserInfoFlags: ");
         pw.println(UserInfo.flagsToString(mDefaultUserInfoPropertyFlags));
-        pw.print(prefix); pw.print("mLabel: "); pw.println(mLabel);
         mDefaultUserProperties.println(pw, prefix);
 
         final String restrictionsPrefix = prefix + "    ";
@@ -392,6 +404,8 @@
         pw.println(mBadgeColors != null ? mBadgeColors.length : "0(null)");
         pw.print(prefix); pw.print("mDarkThemeBadgeColors.length: ");
         pw.println(mDarkThemeBadgeColors != null ? mDarkThemeBadgeColors.length : "0(null)");
+        pw.print(prefix); pw.print("mLabels.length: ");
+        pw.println(mLabels != null ? mLabels.length : "0(null)");
     }
 
     /** Builder for a {@link UserTypeDetails}; see that class for documentation. */
@@ -408,7 +422,7 @@
         private @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters =
                 null;
         private int mEnabled = 1;
-        private int mLabel = Resources.ID_NULL;
+        private @Nullable int[] mLabels = null;
         private @Nullable int[] mBadgeLabels = null;
         private @Nullable int[] mBadgeColors = null;
         private @Nullable int[] mDarkThemeBadgeColors = null;
@@ -488,8 +502,8 @@
             return this;
         }
 
-        public Builder setLabel(int label) {
-            mLabel = label;
+        public Builder setLabels(@StringRes int ... labels) {
+            mLabels = labels;
             return this;
         }
 
@@ -562,7 +576,7 @@
                     mMaxAllowed,
                     mBaseType,
                     mDefaultUserInfoPropertyFlags,
-                    mLabel,
+                    mLabels,
                     mMaxAllowedPerParent,
                     mIconBadge,
                     mBadgePlain,
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 7da76c1..4ef8cb7 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -128,7 +128,7 @@
                 .setName(USER_TYPE_PROFILE_CLONE)
                 .setBaseType(FLAG_PROFILE)
                 .setMaxAllowedPerParent(1)
-                .setLabel(0)
+                .setLabels(R.string.profile_label_clone)
                 .setIconBadge(com.android.internal.R.drawable.ic_clone_icon_badge)
                 .setBadgePlain(com.android.internal.R.drawable.ic_clone_badge)
                 // Clone doesn't use BadgeNoBackground, so just set to BadgePlain as a placeholder.
@@ -154,6 +154,10 @@
                                 UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
                         .setCrossProfileIntentResolutionStrategy(UserProperties
                                 .CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING)
+                        .setShowInQuietMode(
+                                UserProperties.SHOW_IN_QUIET_MODE_DEFAULT)
+                        .setShowInSharingSurfaces(
+                                UserProperties.SHOW_IN_SHARING_SURFACES_WITH_PARENT)
                         .setMediaSharedWithParent(true)
                         .setCredentialShareableWithParent(true)
                         .setDeleteAppWithParent(true));
@@ -169,7 +173,10 @@
                 .setBaseType(FLAG_PROFILE)
                 .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE)
                 .setMaxAllowedPerParent(1)
-                .setLabel(0)
+                .setLabels(
+                        R.string.profile_label_work,
+                        R.string.profile_label_work_2,
+                        R.string.profile_label_work_3)
                 .setIconBadge(com.android.internal.R.drawable.ic_corp_icon_badge_case)
                 .setBadgePlain(com.android.internal.R.drawable.ic_corp_badge_case)
                 .setBadgeNoBackground(com.android.internal.R.drawable.ic_corp_badge_no_background)
@@ -193,6 +200,10 @@
                         .setStartWithParent(true)
                         .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
                         .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
+                        .setShowInQuietMode(
+                                UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
+                        .setShowInSharingSurfaces(
+                                UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
                         .setAuthAlwaysRequiredToDisableQuietMode(false)
                         .setCredentialShareableWithParent(true));
     }
@@ -209,7 +220,10 @@
                 .setName(USER_TYPE_PROFILE_TEST)
                 .setBaseType(FLAG_PROFILE)
                 .setMaxAllowedPerParent(2)
-                .setLabel(0)
+                .setLabels(
+                        R.string.profile_label_test,
+                        R.string.profile_label_test,
+                        R.string.profile_label_test)
                 .setIconBadge(com.android.internal.R.drawable.ic_test_icon_badge_experiment)
                 .setBadgePlain(com.android.internal.R.drawable.ic_test_badge_experiment)
                 .setBadgeNoBackground(com.android.internal.R.drawable.ic_test_badge_no_background)
@@ -240,7 +254,7 @@
                 .setBaseType(FLAG_PROFILE)
                 .setMaxAllowed(1)
                 .setEnabled(UserManager.isCommunalProfileEnabled() ? 1 : 0)
-                .setLabel(0)
+                .setLabels(R.string.profile_label_communal)
                 .setIconBadge(com.android.internal.R.drawable.ic_test_icon_badge_experiment)
                 .setBadgePlain(com.android.internal.R.drawable.ic_test_badge_experiment)
                 .setBadgeNoBackground(com.android.internal.R.drawable.ic_test_badge_no_background)
@@ -276,7 +290,7 @@
                 .setName(USER_TYPE_PROFILE_PRIVATE)
                 .setBaseType(FLAG_PROFILE)
                 .setMaxAllowedPerParent(1)
-                .setLabel(0)
+                .setLabels(R.string.profile_label_private)
                 .setIconBadge(com.android.internal.R.drawable.ic_private_profile_icon_badge)
                 .setBadgePlain(com.android.internal.R.drawable.ic_private_profile_badge)
                 // Private Profile doesn't use BadgeNoBackground, so just set to BadgePlain
@@ -298,7 +312,10 @@
                         .setMediaSharedWithParent(false)
                         .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
                         .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
-                        .setHideInSettingsInQuietMode(true)
+                        .setShowInQuietMode(
+                                UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
+                        .setShowInSharingSurfaces(
+                                UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
                         .setCrossProfileIntentFilterAccessControl(
                                 UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
                         .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT));
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index 2ab7db4..459e2cf 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -28,9 +28,9 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.ApexManager;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 
 import libcore.io.IoUtils;
 
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index 3b10c7f..1c751e0 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -35,12 +35,12 @@
 import android.util.Slog;
 
 import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
index 8b5719a..f4e187f 100644
--- a/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
@@ -21,7 +21,7 @@
 import android.os.Build;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 
 /**
  * Updates a package to ensure that if it targets <= P that the android.hidl.base-V1.0-java
diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
index adaa04c..34880a8d 100644
--- a/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
@@ -16,7 +16,7 @@
 package com.android.server.pm.parsing.library;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 
 /**
  * Updates a package to remove dependency on android.net.ipsec.ike library.
diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
index 3b29d1f..97e3020 100644
--- a/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
@@ -29,8 +29,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 /**
diff --git a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
index 041b77b..0c38c60 100644
--- a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
@@ -19,9 +19,9 @@
 import android.util.ArrayMap;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.modules.utils.build.UnboundedSdkLevel;
 import com.android.server.SystemConfig;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 
 /**
  * Updates packages to add or remove dependencies on shared libraries as per attributes
diff --git a/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java
index b47a768..1c6c1fb 100644
--- a/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java
@@ -16,7 +16,7 @@
 package com.android.server.pm.parsing.library;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 
 /**
  * Updates a package to remove dependency on com.google.android.maps library.
diff --git a/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java b/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
index ac65c8c..b558981 100644
--- a/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
@@ -20,7 +20,7 @@
 import android.os.Build;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 /**
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
index 3da7141..fe9cd0e 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
@@ -24,9 +24,9 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.SystemConfig;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
index a9c22d9..a5af005 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
@@ -19,8 +19,8 @@
 import android.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 0eb2bbd..61be6e1 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -29,17 +29,18 @@
 import android.os.incremental.IncrementalManager;
 
 import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.pm.parsing.pkg.AndroidPackageHidden;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
 import com.android.internal.pm.pkg.component.ParsedProvider;
 import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.parsing.ParsingPackageHidden;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.SystemConfig;
 import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.parsing.ParsingPackageHidden;
 
 import java.io.IOException;
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index c8ac698..85d95ea 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -50,6 +50,10 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.AndroidPackageHidden;
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.AndroidPackageSplitImpl;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedApexSystemService;
 import com.android.internal.pm.pkg.component.ParsedAttribution;
@@ -63,6 +67,8 @@
 import com.android.internal.pm.pkg.component.ParsedProvider;
 import com.android.internal.pm.pkg.component.ParsedService;
 import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackageHidden;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DataClass;
@@ -71,7 +77,6 @@
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.AndroidPackageSplit;
-import com.android.server.pm.pkg.AndroidPackageSplitImpl;
 import com.android.server.pm.pkg.SELinuxUtil;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
 import com.android.server.pm.pkg.component.ParsedActivityImpl;
@@ -84,8 +89,6 @@
 import com.android.server.pm.pkg.component.ParsedProviderImpl;
 import com.android.server.pm.pkg.component.ParsedServiceImpl;
 import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageHidden;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 6f6bb45..f7f76aa 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -807,7 +807,7 @@
                     getDefaultSystemHandlerActivityPackage(pm,
                             SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
                     userId, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
-                    NOTIFICATION_PERMISSIONS);
+                    NOTIFICATION_PERMISSIONS, PHONE_PERMISSIONS);
         }
 
         // Voice recognition
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index fc74a195..c737283 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -22,9 +22,9 @@
 import android.content.pm.SigningDetails;
 import android.util.SparseArray;
 
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
 import com.android.server.pm.InstallSource;
 import com.android.server.pm.PackageKeySetData;
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal;
 import com.android.server.pm.permission.LegacyPermissionState;
 
 import java.util.UUID;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java b/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
index 041edaa..019ca13 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
@@ -32,9 +32,9 @@
 import com.android.internal.pm.pkg.component.ParsedComponent;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
 import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.pkg.PackageUserState;
 import com.android.server.pm.pkg.PackageUserStateUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
index 1497684..dd54cfc 100644
--- a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
+++ b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
@@ -26,8 +26,8 @@
 import android.util.ArraySet;
 
 import com.android.internal.R;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.SystemConfig;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
index 5709cbb..64985bd 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@@ -49,8 +49,8 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
index c6b9b1a..9322cf0 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
@@ -31,7 +31,7 @@
 import android.util.TypedValue;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
index 9792a91..a711694 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
@@ -27,7 +27,7 @@
 
 import com.android.internal.R;
 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 
 import org.xmlpull.v1.XmlPullParserException;
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
index 5e67bbf..e5e214d 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
@@ -32,7 +32,7 @@
 
 import com.android.internal.R;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
index 6c22f82..8268f0f 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
@@ -33,7 +33,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
 import com.android.internal.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
index 0f2b49b..4b45d37 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
@@ -33,7 +33,7 @@
 import com.android.internal.R;
 import com.android.internal.pm.pkg.component.ParsedPermission;
 import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
index 766fb90..a849549 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
@@ -28,9 +28,9 @@
 
 import com.android.internal.R;
 import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.XmlUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
index b66db4f..0b28a12 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
@@ -35,7 +35,7 @@
 
 import com.android.internal.R;
 import com.android.internal.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
index 1b42184..171ef59 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
@@ -33,7 +33,7 @@
 
 import com.android.internal.R;
 import com.android.internal.pm.pkg.component.ParsedService;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index e4594c5..722350a 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -89,6 +89,7 @@
 
 import com.android.internal.R;
 import com.android.internal.os.ClassLoaderFactory;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedApexSystemService;
 import com.android.internal.pm.pkg.component.ParsedAttribution;
@@ -102,11 +103,11 @@
 import com.android.internal.pm.pkg.component.ParsedProvider;
 import com.android.internal.pm.pkg.component.ParsedService;
 import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.SharedUidMigration;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.permission.CompatibilityPermissionInfo;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
 import com.android.server.pm.pkg.component.ComponentParseUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
index 2cfffb3..1d15955 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
@@ -31,6 +31,7 @@
 import android.util.Slog;
 
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.internal.util.Parcelling;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 986735f..73c4224 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3537,7 +3537,8 @@
                     mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness);
 
                     Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
-                    intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION
+                            | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
                     intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
                     startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
                     logKeyboardSystemsEvent(event, KeyboardLogEvent.getBrightnessEvent(keyCode));
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 68f554c..ea8a801 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.server.webkit;
 
+import static android.webkit.Flags.updateServiceV2;
+
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.content.Context;
@@ -237,18 +239,30 @@
 
     @Override
     public int getMultiProcessSetting(Context context) {
-        return Settings.Global.getInt(context.getContentResolver(),
-                                      Settings.Global.WEBVIEW_MULTIPROCESS, 0);
+        if (updateServiceV2()) {
+            throw new IllegalStateException(
+                    "getMultiProcessSetting shouldn't be called if update_service_v2 flag is set.");
+        }
+        return Settings.Global.getInt(
+                context.getContentResolver(), Settings.Global.WEBVIEW_MULTIPROCESS, 0);
     }
 
     @Override
     public void setMultiProcessSetting(Context context, int value) {
-        Settings.Global.putInt(context.getContentResolver(),
-                               Settings.Global.WEBVIEW_MULTIPROCESS, value);
+        if (updateServiceV2()) {
+            throw new IllegalStateException(
+                    "setMultiProcessSetting shouldn't be called if update_service_v2 flag is set.");
+        }
+        Settings.Global.putInt(
+                context.getContentResolver(), Settings.Global.WEBVIEW_MULTIPROCESS, value);
     }
 
     @Override
     public void notifyZygote(boolean enableMultiProcess) {
+        if (updateServiceV2()) {
+            throw new IllegalStateException(
+                    "notifyZygote shouldn't be called if update_service_v2 flag is set.");
+        }
         WebViewZygote.setMultiprocessEnabled(enableMultiProcess);
     }
 
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index b3672ec..b12da61 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -157,8 +157,13 @@
         public void onShellCommand(FileDescriptor in, FileDescriptor out,
                 FileDescriptor err, String[] args, ShellCallback callback,
                 ResultReceiver resultReceiver) {
-            (new WebViewUpdateServiceShellCommand(this)).exec(
-                    this, in, out, err, args, callback, resultReceiver);
+            if (updateServiceV2()) {
+                (new WebViewUpdateServiceShellCommand2(this))
+                        .exec(this, in, out, err, args, callback, resultReceiver);
+            } else {
+                (new WebViewUpdateServiceShellCommand(this))
+                        .exec(this, in, out, err, args, callback, resultReceiver);
+            }
         }
 
 
@@ -275,18 +280,31 @@
 
         @Override // Binder call
         public boolean isMultiProcessEnabled() {
+            if (updateServiceV2()) {
+                throw new IllegalStateException(
+                        "isMultiProcessEnabled shouldn't be called if update_service_v2 flag is"
+                                + " set.");
+            }
             return WebViewUpdateService.this.mImpl.isMultiProcessEnabled();
         }
 
         @Override // Binder call
         public void enableMultiProcess(boolean enable) {
-            if (getContext().checkCallingPermission(
-                        android.Manifest.permission.WRITE_SECURE_SETTINGS)
+            if (updateServiceV2()) {
+                throw new IllegalStateException(
+                        "enableMultiProcess shouldn't be called if update_service_v2 flag is set.");
+            }
+            if (getContext()
+                            .checkCallingPermission(
+                                    android.Manifest.permission.WRITE_SECURE_SETTINGS)
                     != PackageManager.PERMISSION_GRANTED) {
-                String msg = "Permission Denial: enableMultiProcess() from pid="
-                        + Binder.getCallingPid()
-                        + ", uid=" + Binder.getCallingUid()
-                        + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS;
+                String msg =
+                        "Permission Denial: enableMultiProcess() from pid="
+                                + Binder.getCallingPid()
+                                + ", uid="
+                                + Binder.getCallingUid()
+                                + " requires "
+                                + android.Manifest.permission.WRITE_SECURE_SETTINGS;
                 Slog.w(TAG, msg);
                 throw new SecurityException(msg);
             }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index e618c7e..89cb4c8 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -23,6 +23,7 @@
 import android.os.AsyncTask;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.Slog;
 import android.webkit.UserPackage;
 import android.webkit.WebViewFactory;
@@ -70,10 +71,6 @@
         WebViewPackageMissingException(String message) {
             super(message);
         }
-
-        WebViewPackageMissingException(Exception e) {
-            super(e);
-        }
     }
 
     private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
@@ -85,9 +82,6 @@
     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;
 
@@ -166,7 +160,6 @@
 
     @Override
     public void prepareWebViewInSystemServer() {
-        mSystemInterface.notifyZygote(isMultiProcessEnabled());
         try {
             synchronized (mLock) {
                 mCurrentWebViewPackage = findPreferredWebViewPackage();
@@ -366,14 +359,10 @@
 
         // 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);
-        }
+        AsyncTask.THREAD_POOL_EXECUTOR.execute(this::startZygoteWhenReady);
     }
 
-    /**
-     * Fetch only the currently valid WebView packages.
-     **/
+    /** Fetch only the currently valid WebView packages. */
     @Override
     public WebViewProviderInfo[] getValidWebViewPackages() {
         ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
@@ -632,62 +621,56 @@
 
     @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;
-        }
+        throw new IllegalStateException(
+                "isMultiProcessEnabled shouldn't be called if update_service_v2 flag is set.");
     }
 
     @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);
-        }
+        throw new IllegalStateException(
+                "enableMultiProcess shouldn't be called if update_service_v2 flag is set.");
     }
 
-    /**
-     * Dump the state of this Service.
-     */
+    /** 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(
+                        TextUtils.formatSimple(
+                                "  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));
+            pw.println(
+                    TextUtils.formatSimple(
+                            "  Minimum targetSdkVersion: %d", UserPackage.MINIMUM_SUPPORTED_SDK));
+            pw.println(
+                    TextUtils.formatSimple(
+                            "  Minimum WebView version code: %d", mMinimumVersionCode));
+            pw.println(
+                    TextUtils.formatSimple(
+                            "  Number of relros started: %d", mNumRelroCreationsStarted));
+            pw.println(
+                    TextUtils.formatSimple(
+                            "  Number of relros finished: %d", mNumRelroCreationsFinished));
+            pw.println(TextUtils.formatSimple("  WebView package dirty: %b", mWebViewPackageDirty));
+            pw.println(
+                    TextUtils.formatSimple(
+                            "  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));
+                pw.println(
+                        TextUtils.formatSimple(
+                                "  Preferred WebView package (name, version): (%s, %s)",
+                                preferredWebViewPackage.packageName,
+                                preferredWebViewPackage.versionName));
             } catch (WebViewPackageMissingException e) {
-                pw.println(String.format("  Preferred WebView package: none"));
+                pw.println("  Preferred WebView package: none");
             }
 
             dumpAllPackageInformationLocked(pw);
@@ -703,29 +686,36 @@
             PackageInfo systemUserPackageInfo =
                     userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
             if (systemUserPackageInfo == null) {
-                pw.println(String.format("    %s is NOT installed.", provider.packageName));
+                pw.println(
+                        TextUtils.formatSimple("    %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);
+            String packageDetails =
+                    TextUtils.formatSimple(
+                            "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"));
+                boolean installedForAllUsers =
+                        isInstalledAndEnabledForAllUsers(
+                                mSystemInterface.getPackageInfoForProviderAllUsers(
+                                        mContext, provider));
+                pw.println(
+                        TextUtils.formatSimple(
+                                "    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)));
+                pw.println(
+                        TextUtils.formatSimple(
+                                "    Invalid package %s (%s), reason: %s",
+                                systemUserPackageInfo.packageName,
+                                packageDetails,
+                                getInvalidityReason(validity)));
             }
         }
     }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand2.java
new file mode 100644
index 0000000..ce95b18
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand2.java
@@ -0,0 +1,91 @@
+/*
+ * 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.os.RemoteException;
+import android.os.ShellCommand;
+import android.text.TextUtils;
+import android.webkit.IWebViewUpdateService;
+
+import java.io.PrintWriter;
+
+class WebViewUpdateServiceShellCommand2 extends ShellCommand {
+    final IWebViewUpdateService mInterface;
+
+    WebViewUpdateServiceShellCommand2(IWebViewUpdateService service) {
+        mInterface = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+
+        final PrintWriter pw = getOutPrintWriter();
+        try {
+            switch (cmd) {
+                case "set-webview-implementation":
+                    return setWebViewImplementation();
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        } catch (RemoteException e) {
+            pw.println("Remote exception: " + e);
+        }
+        return -1;
+    }
+
+    private int setWebViewImplementation() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        String shellChosenPackage = getNextArg();
+        if (shellChosenPackage == null) {
+            pw.println("Failed to switch, no PACKAGE provided.");
+            pw.println("");
+            helpSetWebViewImplementation();
+            return 1;
+        }
+        String newPackage = mInterface.changeProviderAndSetting(shellChosenPackage);
+        if (shellChosenPackage.equals(newPackage)) {
+            pw.println("Success");
+            return 0;
+        } else {
+            pw.println(
+                    TextUtils.formatSimple(
+                            "Failed to switch to %s, the WebView implementation is now provided by"
+                                    + " %s.",
+                            shellChosenPackage, newPackage));
+            return 1;
+        }
+    }
+
+    public void helpSetWebViewImplementation() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("  set-webview-implementation PACKAGE");
+        pw.println("    Set the WebView implementation to the specified package.");
+    }
+
+    @Override
+    public void onHelp() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("WebView updater commands:");
+        pw.println("  help");
+        pw.println("    Print this help text.");
+        pw.println("");
+        helpSetWebViewImplementation();
+        pw.println();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8498368..75e6faf 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2731,7 +2731,7 @@
      * Receive the splash screen data from shell, sending to client.
      * @param parcelable The data to reconstruct the splash screen view, null mean unable to copy.
      */
-    void onCopySplashScreenFinish(SplashScreenViewParcelable parcelable) {
+    void onCopySplashScreenFinish(@Nullable SplashScreenViewParcelable parcelable) {
         removeTransferSplashScreenTimeout();
         final SurfaceControl windowAnimationLeash = (parcelable == null
                 || mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 34c7eee..6f5c676 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3618,8 +3618,9 @@
      * @hide
      */
     @Override
-    public void onSplashScreenViewCopyFinished(int taskId, SplashScreenViewParcelable parcelable)
-            throws RemoteException {
+    public void onSplashScreenViewCopyFinished(int taskId,
+            @Nullable SplashScreenViewParcelable parcelable)
+                throws RemoteException {
         mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,
                 "copySplashScreenViewFinish()");
         synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 43f3209..be7b855 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -429,9 +429,12 @@
             final TaskFragment prevTFAdjacent = prevTF.getAdjacentTaskFragment();
             if (prevTFAdjacent != null) {
                 if (prevTFAdjacent == currTF) {
-                    // Cannot predict what will happen when app receive back key, skip animation.
                     outPrevActivities.clear();
-                    return false;
+                    // No more activity in task, so it can predict if previous task exists.
+                    // Otherwise, unable to predict what will happen when app receive
+                    // back key, skip animation.
+                    return currentTask.getActivity((below) -> !below.finishing, prevActivity,
+                            false /*includeBoundary*/, true /*traverseTopToBottom*/) == null;
                 } else {
                     final ActivityRecord prevActivityAdjacent =
                             prevTFAdjacent.getTopNonFinishingActivity();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d65d778..522e7d2 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -627,7 +627,7 @@
     void refreshSecureSurfaceState() {
         forAllWindows((w) -> {
             if (w.mHasSurface) {
-                w.mWinAnimator.setSecureLocked(w.isSecureLocked());
+                w.setSecureLocked(w.isSecureLocked());
             }
         }, true /* traverseTopToBottom */);
     }
@@ -2171,12 +2171,16 @@
                     // now, it will take focus briefly which confuses the RecentTasks tracker.
                     rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
                 }
-
+                // Temporarily disable focus when reparenting to avoid intermediate focus change
+                // (because the task is on top and the activity is resumed), which could cause the
+                // task to be added in recents task list unexpectedly.
+                rootTask.setFocusable(false);
                 // There are multiple activities in the task and moving the top activity should
                 // reveal/leave the other activities in their original task.
                 // On the other hand, ActivityRecord#onParentChanged takes care of setting the
                 // up-to-dated root pinned task information on this newly created root task.
                 r.reparent(rootTask, MAX_VALUE, reason);
+                rootTask.setFocusable(true);
 
                 // Ensure the leash of new task is in sync with its current bounds after reparent.
                 rootTask.maybeApplyLastRecentsAnimationTransaction();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 575ae69b..dd2b48b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2327,8 +2327,8 @@
             boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
                     && win.hasWallpaper();
             wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
-            if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) {
-                winAnimator.mSurfaceController.setSecure(win.isSecureLocked());
+            if ((flagChanges & FLAG_SECURE) != 0) {
+                win.setSecureLocked(win.isSecureLocked());
             }
 
             final boolean wasVisible = win.isVisible();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3e43908..e1f1f66 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -109,6 +109,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_INSETS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
 import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
@@ -177,6 +178,7 @@
 import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
 import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
+import static com.android.window.flags.Flags.secureWindowState;
 import static com.android.window.flags.Flags.surfaceTrustedOverlay;
 
 import android.annotation.CallSuper;
@@ -1195,6 +1197,9 @@
         if (surfaceTrustedOverlay() && isWindowTrustedOverlay()) {
             getPendingTransaction().setTrustedOverlay(mSurfaceControl, true);
         }
+        if (secureWindowState()) {
+            getPendingTransaction().setSecure(mSurfaceControl, isSecureLocked());
+        }
     }
 
     void updateTrustedOverlay() {
@@ -3276,7 +3281,9 @@
             // just kill it. And if it is a window of foreground activity, the activity can be
             // restarted automatically if needed.
             Slog.w(TAG, "Exception thrown during dispatchAppVisibility " + this, e);
-            android.os.Process.killProcess(mSession.mPid);
+            if (android.os.Process.getUidForPid(mSession.mPid) == mSession.mUid) {
+                android.os.Process.killProcess(mSession.mPid);
+            }
         }
     }
 
@@ -6040,4 +6047,25 @@
         // Cancel any draw requests during a sync.
         return mPrepareSyncSeqId > 0;
     }
+
+    void setSecureLocked(boolean isSecure) {
+        ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, getName());
+        if (secureWindowState()) {
+            if (mSurfaceControl == null) {
+                return;
+            }
+            getPendingTransaction().setSecure(mSurfaceControl, isSecure);
+        } else {
+            if (mWinAnimator.mSurfaceController == null
+                    || mWinAnimator.mSurfaceController.mSurfaceControl == null) {
+                return;
+            }
+            getPendingTransaction().setSecure(mWinAnimator.mSurfaceController.mSurfaceControl,
+                    isSecure);
+        }
+        if (mDisplayContent != null) {
+            mDisplayContent.refreshImeSecureFlag(getSyncTransaction());
+        }
+        mWmService.scheduleAnimationLocked();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 3aac816..44cd23d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -44,6 +44,7 @@
 import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
 import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
 import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
+import static com.android.window.flags.Flags.secureWindowState;
 
 import android.content.Context;
 import android.graphics.PixelFormat;
@@ -286,8 +287,10 @@
         int flags = SurfaceControl.HIDDEN;
         final WindowManager.LayoutParams attrs = w.mAttrs;
 
-        if (w.isSecureLocked()) {
-            flags |= SurfaceControl.SECURE;
+        if (!secureWindowState()) {
+            if (w.isSecureLocked()) {
+                flags |= SurfaceControl.SECURE;
+            }
         }
 
         if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) {
@@ -488,13 +491,6 @@
         mSurfaceController.setOpaque(isOpaque);
     }
 
-    void setSecureLocked(boolean isSecure) {
-        if (mSurfaceController == null) {
-            return;
-        }
-        mSurfaceController.setSecure(isSecure);
-    }
-
     void setColorSpaceAgnosticLocked(boolean agnostic) {
         if (mSurfaceController == null) {
             return;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index d348491..4456a94 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -24,7 +24,6 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
@@ -152,24 +151,6 @@
         mService.scheduleAnimationLocked();
     }
 
-    void setSecure(boolean isSecure) {
-        ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, title);
-
-        if (mSurfaceControl == null) {
-            return;
-        }
-        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setSecureLocked");
-
-        final SurfaceControl.Transaction t = mAnimator.mWin.getPendingTransaction();
-        t.setSecure(mSurfaceControl, isSecure);
-
-        final DisplayContent dc = mAnimator.mWin.mDisplayContent;
-        if (dc != null) {
-            dc.refreshImeSecureFlag(t);
-        }
-        mService.scheduleAnimationLocked();
-    }
-
     void setColorSpaceAgnostic(SurfaceControl.Transaction t, boolean agnostic) {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isColorSpaceAgnostic=%b: %s", agnostic, title);
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9b62a2c..e0a2f30 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -18182,7 +18182,8 @@
         private static boolean hasAccountFeatures(AccountManager am, Account account,
                 String[] features) {
             try {
-                return am.hasFeatures(account, features, null, null).getResult();
+                return am.hasFeatures(account, features, null, null)
+                        .getResult(30, TimeUnit.SECONDS);
             } catch (Exception e) {
                 Slogf.w(LOG_TAG, "Failed to get account feature", e);
                 return false;
diff --git a/services/permission/OWNERS b/services/permission/OWNERS
index e464038..487c992 100644
--- a/services/permission/OWNERS
+++ b/services/permission/OWNERS
@@ -1,5 +1,3 @@
 #Bug component: 137825
 
-joecastro@google.com
-ntmyren@google.com
-zhanghai@google.com
+include platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index 4e46836..d62da1a 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -57,11 +57,11 @@
 
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
 import com.android.server.LocalServices;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowUserManager;
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 8d76fdd..3011fa1 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -24,6 +24,8 @@
 import android.os.Binder
 import android.os.UserHandle
 import android.util.ArrayMap
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.parsing.pkg.ParsedPackage
 import com.android.internal.pm.pkg.component.ParsedActivity
 import com.android.server.pm.AppsFilterImpl
 import com.android.server.pm.PackageManagerService
@@ -36,9 +38,7 @@
 import com.android.server.pm.SharedLibrariesImpl
 import com.android.server.pm.UserManagerInternal
 import com.android.server.pm.UserManagerService
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
 import com.android.server.pm.parsing.pkg.PackageImpl
-import com.android.server.pm.parsing.pkg.ParsedPackage
 import com.android.server.pm.pkg.AndroidPackage
 import com.android.server.pm.resolution.ComponentResolver
 import com.android.server.pm.snapshot.PackageDataSnapshot
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
index 25146a8..3461bb6 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
@@ -49,11 +49,12 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.om.OverlayReferenceMapper;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.component.ParsedActivityImpl;
 import com.android.server.pm.pkg.component.ParsedComponentImpl;
@@ -62,7 +63,6 @@
 import com.android.server.pm.pkg.component.ParsedPermissionImpl;
 import com.android.server.pm.pkg.component.ParsedProviderImpl;
 import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 import com.android.server.utils.WatchableTester;
 
 import org.junit.Before;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index 2810145..a0dc2b6 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -63,10 +63,10 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.permission.persistence.RuntimePermissionsPersistence;
 import com.android.server.LocalServices;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.permission.LegacyPermissionDataProvider;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.ArchiveState;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
index 7552800..9c48af8 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
@@ -72,7 +72,7 @@
 
 import com.android.compatibility.common.util.CddTest;
 import com.android.internal.content.InstallLocationUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.test.service.server.R;
 
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
index 7c28e13..ea88ec2 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
@@ -58,6 +58,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedApexSystemService;
 import com.android.internal.pm.pkg.component.ParsedComponent;
@@ -68,6 +69,7 @@
 import com.android.internal.pm.pkg.component.ParsedProvider;
 import com.android.internal.pm.pkg.component.ParsedService;
 import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.parsing.PackageCacher;
 import com.android.server.pm.parsing.PackageInfoUtils;
@@ -75,7 +77,6 @@
 import com.android.server.pm.parsing.TestPackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.permission.CompatibilityPermissionInfo;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageUserStateInternal;
@@ -88,7 +89,6 @@
 import com.android.server.pm.pkg.component.ParsedProviderImpl;
 import com.android.server.pm.pkg.component.ParsedServiceImpl;
 import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
index 38d01d0..8a74e24 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
@@ -21,9 +21,9 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.TestPackageParser2;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 
 import junit.framework.Assert;
 
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanRequestBuilder.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanRequestBuilder.java
index 1c3673e..2a8e5b1 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanRequestBuilder.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanRequestBuilder.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.os.UserHandle;
 
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 class ScanRequestBuilder {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
index e2939c1..decb44c 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
@@ -50,13 +50,13 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Pair;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 
 import org.hamcrest.BaseMatcher;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index 7123c20..b102ab4 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -38,12 +38,12 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.component.ParsedComponent;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
 import com.android.internal.pm.pkg.component.ParsedPermission;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.PackageManagerException;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.component.ParsedActivityUtils;
 import com.android.server.pm.pkg.component.ParsedPermissionUtils;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
index 3b926c2..67b91d2 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
@@ -18,12 +18,11 @@
 
 import android.annotation.RawRes
 import android.content.Context
-import com.android.server.pm.pkg.parsing.ParsingPackage
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import android.content.pm.parsing.result.ParseResult
 import android.platform.test.annotations.Presubmit
 import androidx.test.InstrumentationRegistry
-import com.android.server.pm.parsing.pkg.ParsedPackage
+import com.android.internal.pm.parsing.pkg.ParsedPackage
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import com.android.server.pm.test.service.server.R
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
index f376e73..6cd7123 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
@@ -24,8 +24,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
index 9248da6..27fd781 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
@@ -21,8 +21,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
index 23a2c20..b13d6de 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
@@ -23,8 +23,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
index 2060caa..fa69f84 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
@@ -24,9 +24,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.library.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
index b3ad861..856013a 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
@@ -22,9 +22,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.SystemConfig;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Before;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
index 558c0e8..ae5ea21 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
@@ -21,8 +21,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
index 7a2ac75..e126ffc 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
@@ -23,8 +23,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
index c4b8e6f..d0b0cf8 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -28,11 +28,11 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 
 import org.junit.Assume;
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
index 33fc261..d60c457 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import java.util.function.Supplier;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
index 8918233..c141c03 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
@@ -23,9 +23,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
index 3e9ec0e..a58604b 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
@@ -23,9 +23,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
index 766ab94..9341e9d 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -21,9 +21,9 @@
 import android.os.Build
 import android.os.PatternMatcher
 import android.util.ArraySet
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
 import com.android.server.SystemConfig
 import com.android.server.compat.PlatformCompat
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
 import com.android.server.pm.pkg.AndroidPackage
 import com.android.server.pm.pkg.component.ParsedActivityImpl
 import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 9fbf86e..a737b90 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -28,9 +28,8 @@
 import android.util.IndentingPrintWriter
 import android.util.SparseArray
 import androidx.test.platform.app.InstrumentationRegistry
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
 import com.android.server.pm.Computer
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
-import com.android.server.pm.pkg.AndroidPackage
 import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.pm.pkg.PackageUserStateInternal
 import com.android.server.pm.pkg.component.ParsedActivityImpl
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 47d9196..f38df22 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -29,7 +29,7 @@
 import android.os.Process
 import android.util.ArraySet
 import android.util.SparseArray
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
 import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.pm.pkg.PackageUserStateInternal
 import com.android.server.pm.pkg.component.ParsedActivityImpl
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index 98d7801..874e0d2 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -36,9 +36,8 @@
 import android.util.ArraySet
 import android.util.SparseArray
 import android.util.Xml
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
 import com.android.server.pm.Computer
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
-import com.android.server.pm.pkg.AndroidPackage
 import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.pm.pkg.PackageUserStateInternal
 import com.android.server.pm.pkg.component.ParsedActivityImpl
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 4a211df..3207e6c 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -24,7 +24,7 @@
 import android.os.Process
 import android.util.ArraySet
 import android.util.SparseArray
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
 import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.pm.pkg.PackageUserStateInternal
 import com.android.server.pm.pkg.component.ParsedActivityImpl
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index d54d608..a90b7d5 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -26,8 +26,7 @@
 import android.os.Process
 import android.util.ArraySet
 import android.util.SparseArray
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
-import com.android.server.pm.pkg.AndroidPackage
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
 import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.pm.pkg.PackageUserStateInternal
 import com.android.server.pm.pkg.component.ParsedActivityImpl
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
new file mode 100644
index 0000000..3f72364
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+private const val BASE_REFRESH_RATE = 60f
+private const val OTHER_BASE_REFRESH_RATE = 90f
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BaseModeRefreshRateVoteTest {
+
+    private lateinit var baseModeVote: BaseModeRefreshRateVote
+
+    @Before
+    fun setUp() {
+        baseModeVote = BaseModeRefreshRateVote(BASE_REFRESH_RATE)
+    }
+
+    @Test
+    fun `updates summary with base mode refresh rate if not set`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+
+        baseModeVote.updateSummary(summary)
+
+        assertThat(summary.appRequestBaseModeRefreshRate).isEqualTo(BASE_REFRESH_RATE)
+    }
+
+    @Test
+    fun `keeps summary base mode refresh rate if set`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.appRequestBaseModeRefreshRate = OTHER_BASE_REFRESH_RATE
+
+        baseModeVote.updateSummary(summary)
+
+        assertThat(summary.appRequestBaseModeRefreshRate).isEqualTo(OTHER_BASE_REFRESH_RATE)
+    }
+
+    @Test
+    fun `keeps summary with base mode refresh rate if vote refresh rate is negative`() {
+        val invalidBaseModeVote = BaseModeRefreshRateVote(-10f)
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+
+        invalidBaseModeVote.updateSummary(summary)
+
+        assertThat(summary.appRequestBaseModeRefreshRate).isZero()
+    }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
new file mode 100644
index 0000000..7f8da88
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnit
+
+
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CombinedVoteTest {
+    private lateinit var combinedVote: CombinedVote
+
+    @get:Rule
+    val mockitoRule = MockitoJUnit.rule()
+
+    private val mockVote1 = mock<Vote>()
+    private val mockVote2 = mock<Vote>()
+
+    @Before
+    fun setUp() {
+        combinedVote = CombinedVote(listOf(mockVote1, mockVote2))
+    }
+
+    @Test
+    fun `delegates update to children`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+
+        combinedVote.updateSummary(summary)
+
+        verify(mockVote1).updateSummary(summary)
+        verify(mockVote2).updateSummary(summary)
+    }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
new file mode 100644
index 0000000..c624325
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(TestParameterInjector::class)
+class DisableRefreshRateSwitchingVoteTest {
+
+    @Test
+    fun `disabled refresh rate switching is not changed`(
+            @TestParameter voteDisableSwitching: Boolean
+    ) {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.disableRefreshRateSwitching = true
+        val vote = DisableRefreshRateSwitchingVote(voteDisableSwitching)
+
+        vote.updateSummary(summary)
+
+        assertThat(summary.disableRefreshRateSwitching).isTrue()
+    }
+
+    @Test
+    fun `disables refresh rate switching if requested`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        val vote = DisableRefreshRateSwitchingVote(true)
+
+        vote.updateSummary(summary)
+
+        assertThat(summary.disableRefreshRateSwitching).isTrue()
+    }
+
+    @Test
+    fun `does not disable refresh rate switching if not requested`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        val vote = DisableRefreshRateSwitchingVote(false)
+
+        vote.updateSummary(summary)
+
+        assertThat(summary.disableRefreshRateSwitching).isFalse()
+    }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 6798a2d..d085923 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -28,7 +28,6 @@
 import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.server.display.mode.Vote.INVALID_SIZE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -1193,7 +1192,9 @@
         assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
         assertThat(vote).isNotNull();
-        assertThat(vote.disableRefreshRateSwitching).isTrue();
+        assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+        DisableRefreshRateSwitchingVote disableVote = (DisableRefreshRateSwitchingVote) vote;
+        assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
     }
 
     @Test
@@ -1272,7 +1273,9 @@
         assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
         assertThat(vote).isNotNull();
-        assertThat(vote.disableRefreshRateSwitching).isTrue();
+        assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+        DisableRefreshRateSwitchingVote disableVote = (DisableRefreshRateSwitchingVote) vote;
+        assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
 
         // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
         // parameter to the necessary threshold
@@ -1341,7 +1344,9 @@
         assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
         assertThat(vote).isNotNull();
-        assertThat(vote.disableRefreshRateSwitching).isTrue();
+        assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+        DisableRefreshRateSwitchingVote disableVote = (DisableRefreshRateSwitchingVote) vote;
+        assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
     }
 
     @Test
@@ -1424,7 +1429,9 @@
         assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
         assertThat(vote).isNotNull();
-        assertThat(vote.disableRefreshRateSwitching).isTrue();
+        assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+        DisableRefreshRateSwitchingVote disableVote = (DisableRefreshRateSwitchingVote) vote;
+        assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
 
         // Set critical and check new refresh rate
         Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
@@ -1436,7 +1443,9 @@
         assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
         assertThat(vote).isNotNull();
-        assertThat(vote.disableRefreshRateSwitching).isTrue();
+        assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+        disableVote = (DisableRefreshRateSwitchingVote) vote;
+        assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
     }
 
     @Test
@@ -1519,7 +1528,9 @@
         assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
         assertThat(vote).isNotNull();
-        assertThat(vote.disableRefreshRateSwitching).isTrue();
+        assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+        DisableRefreshRateSwitchingVote disableVote = (DisableRefreshRateSwitchingVote) vote;
+        assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
 
         // Set critical and check new refresh rate
         Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
@@ -1531,7 +1542,9 @@
         assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
         assertThat(vote).isNotNull();
-        assertThat(vote.disableRefreshRateSwitching).isTrue();
+        assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+        disableVote = (DisableRefreshRateSwitchingVote) vote;
+        assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
     }
 
     @Test
@@ -1877,61 +1890,43 @@
         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0, 0);
 
-        Vote appRequestRefreshRate =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertNotNull(appRequestRefreshRate);
-        assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero();
-        assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity();
-        assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero();
-        assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity();
-        assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
-        assertThat(appRequestRefreshRate.appRequestBaseModeRefreshRate)
-                .isWithin(FLOAT_TOLERANCE).of(60);
-        assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
-        assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+        assertNotNull(vote);
+        assertThat(vote).isInstanceOf(BaseModeRefreshRateVote.class);
+        BaseModeRefreshRateVote baseModeVote = (BaseModeRefreshRateVote) vote;
+        assertThat(baseModeVote.mAppRequestBaseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
 
-        Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
-        assertNotNull(appRequestSize);
-        assertThat(appRequestSize.refreshRateRanges.physical.min).isZero();
-        assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity();
-        assertThat(appRequestSize.refreshRateRanges.render.min).isZero();
-        assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity();
-        assertThat(appRequestSize.disableRefreshRateSwitching).isFalse();
-        assertThat(appRequestSize.appRequestBaseModeRefreshRate).isZero();
-        assertThat(appRequestSize.height).isEqualTo(1000);
-        assertThat(appRequestSize.width).isEqualTo(1000);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
+        assertNotNull(vote);
+        assertThat(vote).isInstanceOf(SizeVote.class);
+        SizeVote sizeVote = (SizeVote) vote;
+        assertThat(sizeVote.mHeight).isEqualTo(1000);
+        assertThat(sizeVote.mWidth).isEqualTo(1000);
+        assertThat(sizeVote.mMinHeight).isEqualTo(1000);
+        assertThat(sizeVote.mMinWidth).isEqualTo(1000);
 
-        Vote appRequestRefreshRateRange =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNull(appRequestRefreshRateRange);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+        assertNull(vote);
 
         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 90, 0, 0);
 
-        appRequestRefreshRate =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertNotNull(appRequestRefreshRate);
-        assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero();
-        assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity();
-        assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero();
-        assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity();
-        assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
-        assertThat(appRequestRefreshRate.appRequestBaseModeRefreshRate)
-                .isWithin(FLOAT_TOLERANCE).of(90);
-        assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
-        assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+        assertNotNull(vote);
+        assertThat(vote).isInstanceOf(BaseModeRefreshRateVote.class);
+        baseModeVote = (BaseModeRefreshRateVote) vote;
+        assertThat(baseModeVote.mAppRequestBaseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
 
-        appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
-        assertNotNull(appRequestSize);
-        assertThat(appRequestSize.refreshRateRanges.physical.min).isZero();
-        assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity();
-        assertThat(appRequestSize.refreshRateRanges.render.min).isZero();
-        assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity();
-        assertThat(appRequestSize.height).isEqualTo(1000);
-        assertThat(appRequestSize.width).isEqualTo(1000);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
+        assertNotNull(vote);
+        assertThat(vote).isInstanceOf(SizeVote.class);
+        sizeVote = (SizeVote) vote;
+        assertThat(sizeVote.mHeight).isEqualTo(1000);
+        assertThat(sizeVote.mWidth).isEqualTo(1000);
+        assertThat(sizeVote.mMinHeight).isEqualTo(1000);
+        assertThat(sizeVote.mMinWidth).isEqualTo(1000);
 
-        appRequestRefreshRateRange =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNull(appRequestRefreshRateRange);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+        assertNull(vote);
     }
 
     @Test
@@ -1945,17 +1940,12 @@
         Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
         assertNull(appRequestSize);
 
-        Vote appRequestRefreshRateRange =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNotNull(appRequestRefreshRateRange);
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                .isPositiveInfinity();
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
-                .isWithin(FLOAT_TOLERANCE).of(60);
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max).isAtLeast(90);
-        assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
-        assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+        assertNotNull(vote);
+        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+        RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+        assertThat(renderVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
+        assertThat(renderVote.mMaxRefreshRate).isAtLeast(90);
 
         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90, 0);
         appRequestRefreshRate =
@@ -1965,18 +1955,12 @@
         appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
         assertNull(appRequestSize);
 
-        appRequestRefreshRateRange =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNotNull(appRequestRefreshRateRange);
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                .isPositiveInfinity();
-
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
-                .isWithin(FLOAT_TOLERANCE).of(90);
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max).isAtLeast(90);
-        assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
-        assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+        assertNotNull(vote);
+        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+        renderVote = (RefreshRateVote.RenderVote) vote;
+        assertThat(renderVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
+        assertThat(renderVote.mMaxRefreshRate).isAtLeast(90);
     }
 
     @Test
@@ -1990,18 +1974,12 @@
         Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
         assertNull(appRequestSize);
 
-        Vote appRequestRefreshRateRange =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNotNull(appRequestRefreshRateRange);
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                .isPositiveInfinity();
-
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
-                .isWithin(FLOAT_TOLERANCE).of(90);
-        assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
-        assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+        assertNotNull(vote);
+        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+        RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+        assertThat(renderVote.mMinRefreshRate).isZero();
+        assertThat(renderVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
 
         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 60);
         appRequestRefreshRate =
@@ -2011,18 +1989,12 @@
         appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
         assertNull(appRequestSize);
 
-        appRequestRefreshRateRange =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNotNull(appRequestRefreshRateRange);
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                .isPositiveInfinity();
-
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
-                .isWithin(FLOAT_TOLERANCE).of(60);
-        assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
-        assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+        assertNotNull(vote);
+        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+        renderVote = (RefreshRateVote.RenderVote) vote;
+        assertThat(renderVote.mMinRefreshRate).isZero();
+        assertThat(renderVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
     }
 
     @Test
@@ -2046,41 +2018,27 @@
         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90, 90);
 
-        Vote appRequestRefreshRate =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertNotNull(appRequestRefreshRate);
-        assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero();
-        assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity();
-        assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero();
-        assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity();
-        assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
-        assertThat(appRequestRefreshRate.appRequestBaseModeRefreshRate)
-                .isWithin(FLOAT_TOLERANCE).of(60);
-        assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
-        assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+        assertNotNull(vote);
+        assertThat(vote).isInstanceOf(BaseModeRefreshRateVote.class);
+        BaseModeRefreshRateVote baseModeVote = (BaseModeRefreshRateVote) vote;
+        assertThat(baseModeVote.mAppRequestBaseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
 
-        Vote appRequestSize =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
-        assertNotNull(appRequestSize);
-        assertThat(appRequestSize.refreshRateRanges.physical.min).isZero();
-        assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity();
-        assertThat(appRequestSize.refreshRateRanges.render.min).isZero();
-        assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity();
-        assertThat(appRequestSize.height).isEqualTo(1000);
-        assertThat(appRequestSize.width).isEqualTo(1000);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
+        assertNotNull(vote);
+        assertThat(vote).isInstanceOf(SizeVote.class);
+        SizeVote sizeVote = (SizeVote) vote;
+        assertThat(sizeVote.mHeight).isEqualTo(1000);
+        assertThat(sizeVote.mWidth).isEqualTo(1000);
+        assertThat(sizeVote.mMinHeight).isEqualTo(1000);
+        assertThat(sizeVote.mMinWidth).isEqualTo(1000);
 
-        Vote appRequestRefreshRateRange =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNotNull(appRequestRefreshRateRange);
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                .isPositiveInfinity();
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
-                .isWithin(FLOAT_TOLERANCE).of(90);
-        assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
-                .isWithin(FLOAT_TOLERANCE).of(90);
-        assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
-        assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+        assertNotNull(vote);
+        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+        RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+        assertThat(renderVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
+        assertThat(renderVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
     }
 
     @Test
@@ -3150,8 +3108,7 @@
         captor.getValue().onAuthenticationPossible(DISPLAY_ID, true);
 
         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE);
-        assertThat(vote.refreshRateRanges.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
-        assertThat(vote.refreshRateRanges.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
+        assertVoteForPhysicalRefreshRate(vote, 90);
     }
 
     @Test
@@ -3184,8 +3141,7 @@
         captor.getValue().onRequestEnabled(DISPLAY_ID);
 
         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_UDFPS);
-        assertThat(vote.refreshRateRanges.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
-        assertThat(vote.refreshRateRanges.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
+        assertVoteForPhysicalRefreshRate(vote, 90);
     }
 
     @Test
@@ -3257,16 +3213,21 @@
 
     private void assertVoteForPhysicalRefreshRate(Vote vote, float refreshRate) {
         assertThat(vote).isNotNull();
-        final RefreshRateRange expectedRange = new RefreshRateRange(refreshRate, refreshRate);
-        assertThat(vote.refreshRateRanges.physical).isEqualTo(expectedRange);
+        assertThat(vote).isInstanceOf(CombinedVote.class);
+        CombinedVote combinedVote = (CombinedVote) vote;
+        RefreshRateVote.PhysicalVote physicalVote =
+                (RefreshRateVote.PhysicalVote) combinedVote.mVotes.get(0);
+        assertThat(physicalVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(refreshRate);
+        assertThat(physicalVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(refreshRate);
     }
 
     private void assertVoteForRenderFrameRateRange(
             Vote vote, float frameRateLow, float frameRateHigh) {
         assertThat(vote).isNotNull();
-        final RefreshRateRange expectedRange =
-                new RefreshRateRange(frameRateLow, frameRateHigh);
-        assertThat(vote.refreshRateRanges.render).isEqualTo(expectedRange);
+        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+        RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+        assertThat(renderVote.mMinRefreshRate).isEqualTo(frameRateLow);
+        assertThat(renderVote.mMaxRefreshRate).isEqualTo(frameRateHigh);
     }
 
     public static class FakeDeviceConfig extends FakeDeviceConfigInterface {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.mode
+
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val MIN_REFRESH_RATE = 60f
+private const val MAX_REFRESH_RATE = 90f
+
+@SmallTest
+@RunWith(TestParameterInjector::class)
+class PhysicalVoteTest {
+    private lateinit var physicalVote: RefreshRateVote.PhysicalVote
+
+    @Before
+    fun setUp() {
+        physicalVote = RefreshRateVote.PhysicalVote(MIN_REFRESH_RATE, MAX_REFRESH_RATE)
+    }
+
+    @Test
+    fun `updates minPhysicalRefreshRate if summary has less`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.minPhysicalRefreshRate = 45f
+
+        physicalVote.updateSummary(summary)
+
+        assertThat(summary.minPhysicalRefreshRate).isEqualTo(MIN_REFRESH_RATE)
+    }
+
+    @Test
+    fun `does not update minPhysicalRefreshRate if summary has more`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.minPhysicalRefreshRate = 75f
+
+        physicalVote.updateSummary(summary)
+
+        assertThat(summary.minPhysicalRefreshRate).isEqualTo(75f)
+    }
+
+    @Test
+    fun `updates maxPhysicalRefreshRate if summary has more`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.maxPhysicalRefreshRate = 120f
+
+        physicalVote.updateSummary(summary)
+
+        assertThat(summary.maxPhysicalRefreshRate).isEqualTo(MAX_REFRESH_RATE)
+    }
+
+    @Test
+    fun `does not update maxPhysicalRefreshRate if summary has less`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.maxPhysicalRefreshRate = 75f
+
+        physicalVote.updateSummary(summary)
+
+        assertThat(summary.maxPhysicalRefreshRate).isEqualTo(75f)
+    }
+
+    @Test
+    fun `updates maxRenderFrameRate if summary has more`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.maxRenderFrameRate = 120f
+
+        physicalVote.updateSummary(summary)
+
+        assertThat(summary.maxRenderFrameRate).isEqualTo(MAX_REFRESH_RATE)
+    }
+
+    @Test
+    fun `does not update maxRenderFrameRate if summary has less`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.maxRenderFrameRate = 75f
+
+        physicalVote.updateSummary(summary)
+
+        assertThat(summary.maxRenderFrameRate).isEqualTo(75f)
+    }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
new file mode 100644
index 0000000..868a893
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.mode
+
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val MIN_REFRESH_RATE = 60f
+private const val MAX_REFRESH_RATE = 90f
+
+@SmallTest
+@RunWith(TestParameterInjector::class)
+class RenderVoteTest {
+
+    private lateinit var renderVote: RefreshRateVote.RenderVote
+
+    @Before
+    fun setUp() {
+        renderVote = RefreshRateVote.RenderVote(MIN_REFRESH_RATE, MAX_REFRESH_RATE)
+    }
+
+    @Test
+    fun `updates minRenderFrameRate if summary has less`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.minRenderFrameRate = 45f
+
+        renderVote.updateSummary(summary)
+
+        assertThat(summary.minRenderFrameRate).isEqualTo(MIN_REFRESH_RATE)
+    }
+
+    @Test
+    fun `does not update minRenderFrameRate if summary has more`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.minRenderFrameRate = 75f
+
+        renderVote.updateSummary(summary)
+
+        assertThat(summary.minRenderFrameRate).isEqualTo(75f)
+    }
+
+    @Test
+    fun `updates maxRenderFrameRate if summary has more`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.maxRenderFrameRate = 120f
+
+        renderVote.updateSummary(summary)
+
+        assertThat(summary.maxRenderFrameRate).isEqualTo(MAX_REFRESH_RATE)
+    }
+
+    @Test
+    fun `does not update maxRenderFrameRate if summary has less`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.maxRenderFrameRate = 75f
+
+        renderVote.updateSummary(summary)
+
+        assertThat(summary.maxRenderFrameRate).isEqualTo(75f)
+    }
+
+    @Test
+    fun `updates minPhysicalRefreshRate if summary has less`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.minPhysicalRefreshRate = 45f
+
+        renderVote.updateSummary(summary)
+
+        assertThat(summary.minPhysicalRefreshRate).isEqualTo(MIN_REFRESH_RATE)
+    }
+
+    @Test
+    fun `does not update minPhysicalRefreshRate if summary has more`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.minPhysicalRefreshRate = 75f
+
+        renderVote.updateSummary(summary)
+
+        assertThat(summary.minPhysicalRefreshRate).isEqualTo(75f)
+    }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
new file mode 100644
index 0000000..1c631b0
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
@@ -0,0 +1,173 @@
+/*
+ * 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.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+private const val WIDTH = 800
+private const val HEIGHT = 1600
+private const val MIN_WIDTH = 400
+private const val MIN_HEIGHT = 1200
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SizeVoteTest {
+    private lateinit var sizeVote: SizeVote
+
+    @Before
+    fun setUp() {
+        sizeVote = SizeVote(WIDTH, HEIGHT, MIN_WIDTH, MIN_HEIGHT)
+    }
+
+    @Test
+    fun `updates size if width and height not set and display resolution voting disabled`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ false)
+        summary.width = Vote.INVALID_SIZE
+        summary.height = Vote.INVALID_SIZE
+        summary.minWidth = 100
+        summary.minHeight = 200
+
+        sizeVote.updateSummary(summary)
+
+        assertThat(summary.width).isEqualTo(WIDTH)
+        assertThat(summary.height).isEqualTo(HEIGHT)
+        assertThat(summary.minWidth).isEqualTo(MIN_WIDTH)
+        assertThat(summary.minHeight).isEqualTo(MIN_HEIGHT)
+    }
+
+    @Test
+    fun `does not update size if width set and display resolution voting disabled`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ false)
+        summary.width = 150
+        summary.height = Vote.INVALID_SIZE
+        summary.minWidth = 100
+        summary.minHeight = 200
+
+        sizeVote.updateSummary(summary)
+
+        assertThat(summary.width).isEqualTo(150)
+        assertThat(summary.height).isEqualTo(Vote.INVALID_SIZE)
+        assertThat(summary.minWidth).isEqualTo(100)
+        assertThat(summary.minHeight).isEqualTo(200)
+    }
+
+    @Test
+    fun `does not update size if height set and display resolution voting disabled`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ false)
+        summary.width = Vote.INVALID_SIZE
+        summary.height = 250
+        summary.minWidth = 100
+        summary.minHeight = 200
+
+        sizeVote.updateSummary(summary)
+
+        assertThat(summary.width).isEqualTo(Vote.INVALID_SIZE)
+        assertThat(summary.height).isEqualTo(250)
+        assertThat(summary.minWidth).isEqualTo(100)
+        assertThat(summary.minHeight).isEqualTo(200)
+    }
+
+    @Test
+    fun `updates width if summary has more and display resolution voting enabled`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.width = 850
+
+        sizeVote.updateSummary(summary)
+
+        assertThat(summary.width).isEqualTo(WIDTH)
+    }
+
+    @Test
+    fun `does not update width if summary has less and display resolution voting enabled`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.width = 750
+
+        sizeVote.updateSummary(summary)
+
+        assertThat(summary.width).isEqualTo(750)
+    }
+
+    @Test
+    fun `updates height if summary has more and display resolution voting enabled`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.height = 1650
+
+        sizeVote.updateSummary(summary)
+
+        assertThat(summary.height).isEqualTo(HEIGHT)
+    }
+
+    @Test
+    fun `does not update height if summary has less and display resolution voting enabled`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.height = 1550
+
+        sizeVote.updateSummary(summary)
+
+        assertThat(summary.height).isEqualTo(1550)
+    }
+
+    @Test
+    fun `updates minWidth if summary has less and display resolution voting enabled`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.width = 150
+        summary.minWidth = 350
+
+        sizeVote.updateSummary(summary)
+
+        assertThat(summary.minWidth).isEqualTo(MIN_WIDTH)
+    }
+
+    @Test
+    fun `does not update minWidth if summary has more and display resolution voting enabled`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.width = 150
+        summary.minWidth = 450
+
+        sizeVote.updateSummary(summary)
+
+        assertThat(summary.minWidth).isEqualTo(450)
+    }
+
+    @Test
+    fun `updates minHeight if summary has less and display resolution voting enabled`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.width = 150
+        summary.minHeight = 1150
+
+        sizeVote.updateSummary(summary)
+
+        assertThat(summary.minHeight).isEqualTo(MIN_HEIGHT)
+    }
+
+    @Test
+    fun `does not update minHeight if summary has more and display resolution voting enabled`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.width = 150
+        summary.minHeight = 1250
+
+        sizeVote.updateSummary(summary)
+
+        assertThat(summary.minHeight).isEqualTo(1250)
+    }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
index 9ab6ee5..f677401 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
@@ -17,6 +17,8 @@
 package com.android.server.display.mode;
 
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
@@ -102,17 +104,21 @@
         SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
         assertEquals(1, displayVotes.size());
 
-        Vote vote = displayVotes.get(
-                Vote.PRIORITY_SKIN_TEMPERATURE);
-        assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
-        assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
+        Vote vote = displayVotes.get(Vote.PRIORITY_SKIN_TEMPERATURE);
+
+        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+        RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+        assertEquals(0, renderVote.mMinRefreshRate, FLOAT_TOLERANCE);
+        assertEquals(60, renderVote.mMaxRefreshRate, FLOAT_TOLERANCE);
 
         SparseArray<Vote> otherDisplayVotes = mStorage.getVotes(DISPLAY_ID_OTHER);
         assertEquals(1, otherDisplayVotes.size());
 
         vote = otherDisplayVotes.get(Vote.PRIORITY_SKIN_TEMPERATURE);
-        assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
-        assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
+        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+        renderVote = (RefreshRateVote.RenderVote) vote;
+        assertEquals(0, renderVote.mMinRefreshRate, FLOAT_TOLERANCE);
+        assertEquals(60, renderVote.mMaxRefreshRate, FLOAT_TOLERANCE);
     }
 
     @Test
@@ -167,8 +173,10 @@
         SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
         assertEquals(1, displayVotes.size());
         Vote vote = displayVotes.get(Vote.PRIORITY_SKIN_TEMPERATURE);
-        assertEquals(90, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
-        assertEquals(120, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
+        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+        RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+        assertEquals(90, renderVote.mMinRefreshRate, FLOAT_TOLERANCE);
+        assertEquals(120, renderVote.mMaxRefreshRate, FLOAT_TOLERANCE);
         assertEquals(0, mStorage.getVotes(DISPLAY_ID_OTHER).size());
     }
 
@@ -188,8 +196,10 @@
         SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID_ADDED);
 
         Vote vote = displayVotes.get(Vote.PRIORITY_SKIN_TEMPERATURE);
-        assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
-        assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
+        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+        RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+        assertEquals(0, renderVote.mMinRefreshRate, FLOAT_TOLERANCE);
+        assertEquals(60, renderVote.mMaxRefreshRate, FLOAT_TOLERANCE);
     }
 
     @Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
new file mode 100644
index 0000000..cc88003
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SupportedModesVoteTest {
+    private val supportedModes = listOf(
+            SupportedModesVote.SupportedMode(60f, 90f ),
+            SupportedModesVote.SupportedMode(120f, 240f )
+    )
+
+    private val otherMode = SupportedModesVote.SupportedMode(120f, 120f )
+
+    private lateinit var supportedModesVote: SupportedModesVote
+
+    @Before
+    fun setUp() {
+        supportedModesVote = SupportedModesVote(supportedModes)
+    }
+
+    @Test
+    fun `adds supported modes if supportedModes in summary is null`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+
+        supportedModesVote.updateSummary(summary)
+
+        assertThat(summary.supportedModes).containsExactlyElementsIn(supportedModes)
+    }
+
+    @Test
+    fun `does not add supported modes if summary has empty list of modes`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.supportedModes = ArrayList()
+
+        supportedModesVote.updateSummary(summary)
+
+        assertThat(summary.supportedModes).isEmpty()
+    }
+
+    @Test
+    fun `filters out modes that does not match vote`() {
+        val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+        summary.supportedModes = ArrayList(listOf(otherMode, supportedModes[0]))
+
+        supportedModesVote.updateSummary(summary)
+
+        assertThat(summary.supportedModes).containsExactly(supportedModes[0])
+    }
+}
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index be33b1b..46806f3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -55,6 +55,8 @@
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder
 import com.android.internal.R
+import com.android.internal.pm.parsing.pkg.ParsedPackage
+import com.android.internal.pm.pkg.parsing.ParsingPackage
 import com.android.server.LocalManagerRegistry
 import com.android.server.LocalServices
 import com.android.server.LockGuard
@@ -66,10 +68,8 @@
 import com.android.server.pm.dex.DynamicCodeLogger
 import com.android.server.pm.parsing.PackageParser2
 import com.android.server.pm.parsing.pkg.PackageImpl
-import com.android.server.pm.parsing.pkg.ParsedPackage
 import com.android.server.pm.permission.PermissionManagerServiceInternal
 import com.android.server.pm.pkg.AndroidPackage
-import com.android.server.pm.pkg.parsing.ParsingPackage
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import com.android.server.pm.resolution.ComponentResolver
 import com.android.server.pm.snapshot.PackageDataSnapshot
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
index b8f726b..e685c3f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -25,13 +25,13 @@
 import android.os.UserHandle
 import android.util.ArrayMap
 import android.util.PackageUtils
+import com.android.internal.pm.parsing.pkg.ParsedPackage
 import com.android.server.SystemConfig.SharedLibraryEntry
 import com.android.server.compat.PlatformCompat
 import com.android.server.extendedtestutils.wheneverStatic
 import com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME
 import com.android.server.pm.pkg.AndroidPackage
 import com.android.server.pm.parsing.pkg.PackageImpl
-import com.android.server.pm.parsing.pkg.ParsedPackage
 import com.android.server.testutils.any
 import com.android.server.testutils.eq
 import com.android.server.testutils.mock
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index 3a3ab84..dd687fd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -27,9 +27,9 @@
 import android.os.Build;
 import android.platform.test.annotations.Presubmit;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.PackageState;
 
 import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index 57b1225..d70a4fd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -60,7 +60,8 @@
                 .setShowInLauncher(21)
                 .setStartWithParent(false)
                 .setShowInSettings(45)
-                .setHideInSettingsInQuietMode(false)
+                .setShowInSharingSurfaces(78)
+                .setShowInQuietMode(12)
                 .setInheritDevicePolicy(67)
                 .setUseParentsContacts(false)
                 .setCrossProfileIntentFilterAccessControl(10)
@@ -74,7 +75,8 @@
         final UserProperties actualProps = new UserProperties(defaultProps);
         actualProps.setShowInLauncher(14);
         actualProps.setShowInSettings(32);
-        actualProps.setHideInSettingsInQuietMode(true);
+        actualProps.setShowInSharingSurfaces(46);
+        actualProps.setShowInQuietMode(27);
         actualProps.setInheritDevicePolicy(51);
         actualProps.setUseParentsContacts(true);
         actualProps.setCrossProfileIntentFilterAccessControl(20);
@@ -236,8 +238,10 @@
         assertThat(expected.getShowInLauncher()).isEqualTo(actual.getShowInLauncher());
         assertThat(expected.getStartWithParent()).isEqualTo(actual.getStartWithParent());
         assertThat(expected.getShowInSettings()).isEqualTo(actual.getShowInSettings());
-        assertThat(expected.getHideInSettingsInQuietMode())
-                .isEqualTo(actual.getHideInSettingsInQuietMode());
+        assertThat(expected.getShowInSharingSurfaces()).isEqualTo(
+                actual.getShowInSharingSurfaces());
+        assertThat(expected.getShowInQuietMode())
+                .isEqualTo(actual.getShowInQuietMode());
         assertThat(expected.getInheritDevicePolicy()).isEqualTo(actual.getInheritDevicePolicy());
         assertThat(expected.getUseParentsContacts()).isEqualTo(actual.getUseParentsContacts());
         assertThat(expected.getCrossProfileIntentFilterAccessControl())
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 48eb5c6..77f6939 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -29,6 +29,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertTrue;
@@ -91,7 +92,8 @@
                 .setCredentialShareableWithParent(false)
                 .setAuthAlwaysRequiredToDisableQuietMode(true)
                 .setShowInSettings(900)
-                .setHideInSettingsInQuietMode(true)
+                .setShowInSharingSurfaces(20)
+                .setShowInQuietMode(30)
                 .setInheritDevicePolicy(340)
                 .setDeleteAppWithParent(true)
                 .setAlwaysVisible(true);
@@ -107,9 +109,9 @@
                 .setIconBadge(28)
                 .setBadgePlain(29)
                 .setBadgeNoBackground(30)
-                .setLabel(31)
                 .setMaxAllowedPerParent(32)
                 .setStatusBarIcon(33)
+                .setLabels(34, 35, 36)
                 .setDefaultRestrictions(restrictions)
                 .setDefaultSystemSettings(systemSettings)
                 .setDefaultSecureSettings(secureSettings)
@@ -124,9 +126,11 @@
         assertEquals(28, type.getIconBadge());
         assertEquals(29, type.getBadgePlain());
         assertEquals(30, type.getBadgeNoBackground());
-        assertEquals(31, type.getLabel());
         assertEquals(32, type.getMaxAllowedPerParent());
         assertEquals(33, type.getStatusBarIcon());
+        assertEquals(34, type.getLabel(0));
+        assertEquals(35, type.getLabel(1));
+        assertEquals(36, type.getLabel(2));
 
         assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions()));
         assertNotSame(restrictions, type.getDefaultRestrictions());
@@ -164,7 +168,9 @@
         assertTrue(type.getDefaultUserPropertiesReference()
                 .isAuthAlwaysRequiredToDisableQuietMode());
         assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings());
-        assertTrue(type.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
+        assertEquals(20, type.getDefaultUserPropertiesReference().getShowInSharingSurfaces());
+        assertEquals(30,
+                type.getDefaultUserPropertiesReference().getShowInQuietMode());
         assertEquals(340, type.getDefaultUserPropertiesReference()
                 .getInheritDevicePolicy());
         assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent());
@@ -203,7 +209,7 @@
         assertEquals(Resources.ID_NULL, type.getStatusBarIcon());
         assertEquals(Resources.ID_NULL, type.getBadgeLabel(0));
         assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
-        assertEquals(Resources.ID_NULL, type.getLabel());
+        assertEquals(Resources.ID_NULL, type.getLabel(0));
         assertTrue(type.getDefaultRestrictions().isEmpty());
         assertTrue(type.getDefaultSystemSettings().isEmpty());
         assertTrue(type.getDefaultSecureSettings().isEmpty());
@@ -222,7 +228,9 @@
         assertFalse(props.isCredentialShareableWithParent());
         assertFalse(props.getDeleteAppWithParent());
         assertFalse(props.getAlwaysVisible());
-        assertFalse(props.getHideInSettingsInQuietMode());
+        assertEquals(UserProperties.SHOW_IN_LAUNCHER_SEPARATE, props.getShowInSharingSurfaces());
+        assertEquals(UserProperties.SHOW_IN_QUIET_MODE_PAUSED,
+                props.getShowInQuietMode());
 
         assertFalse(type.hasBadge());
     }
@@ -311,8 +319,9 @@
                 .setCredentialShareableWithParent(true)
                 .setAuthAlwaysRequiredToDisableQuietMode(false)
                 .setShowInSettings(20)
-                .setHideInSettingsInQuietMode(false)
                 .setInheritDevicePolicy(21)
+                .setShowInSharingSurfaces(22)
+                .setShowInQuietMode(24)
                 .setDeleteAppWithParent(true)
                 .setAlwaysVisible(false);
 
@@ -354,9 +363,11 @@
         assertFalse(aospType.getDefaultUserPropertiesReference()
                 .isAuthAlwaysRequiredToDisableQuietMode());
         assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings());
-        assertFalse(aospType.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
         assertEquals(21, aospType.getDefaultUserPropertiesReference()
                 .getInheritDevicePolicy());
+        assertEquals(22, aospType.getDefaultUserPropertiesReference().getShowInSharingSurfaces());
+        assertEquals(24,
+                aospType.getDefaultUserPropertiesReference().getShowInQuietMode());
         assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
         assertFalse(aospType.getDefaultUserPropertiesReference().getAlwaysVisible());
 
@@ -403,7 +414,10 @@
         assertTrue(aospType.getDefaultUserPropertiesReference()
                 .isAuthAlwaysRequiredToDisableQuietMode());
         assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings());
-        assertTrue(aospType.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
+        assertEquals(22,
+                aospType.getDefaultUserPropertiesReference().getShowInSharingSurfaces());
+        assertEquals(24,
+                aospType.getDefaultUserPropertiesReference().getShowInQuietMode());
         assertEquals(450, aospType.getDefaultUserPropertiesReference()
                 .getInheritDevicePolicy());
         assertFalse(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 2b6d8ed..c7d80ed 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -343,6 +343,8 @@
         assertThat(mainUserId).isEqualTo(parentProfileInfo.id);
         removeUser(userInfo.id);
         assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
+        assertThat(mUserManager.getProfileLabel()).isEqualTo(
+                Resources.getSystem().getString(userTypeDetails.getLabel(0)));
     }
 
     @MediumTest
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index a8e3c7e..8464969 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -53,10 +53,10 @@
 import androidx.test.runner.AndroidJUnit4;
 import androidx.test.uiautomator.UiDevice;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.After;
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index b2843d8..1bfd43f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -37,10 +37,10 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.servicestests.R;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.parsing.TestPackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Assert;
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index ad9f920..0f87202 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -29,10 +29,10 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 
 import dalvik.system.DelegateLastClassLoader;
 import dalvik.system.DexClassLoader;
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index ebe45a6..d000605 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.webkit;
 
+import static android.webkit.Flags.updateServiceV2;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -25,6 +27,9 @@
 import android.content.pm.Signature;
 import android.os.Build;
 import android.os.Bundle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Base64;
 import android.webkit.UserPackage;
@@ -35,6 +40,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentMatcher;
@@ -55,11 +61,14 @@
 public class WebViewUpdateServiceTest {
     private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName();
 
-    private WebViewUpdateServiceImpl mWebViewUpdateServiceImpl;
+    private WebViewUpdateServiceInterface mWebViewUpdateServiceImpl;
     private TestSystemImpl mTestSystemImpl;
 
     private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary";
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     /**
      * Creates a new instance.
      */
@@ -92,8 +101,13 @@
         TestSystemImpl testing = new TestSystemImpl(packages, numRelros, isDebuggable,
                 multiProcessDefault);
         mTestSystemImpl = Mockito.spy(testing);
-        mWebViewUpdateServiceImpl =
-            new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
+        if (updateServiceV2()) {
+            mWebViewUpdateServiceImpl =
+                    new WebViewUpdateServiceImpl2(null /*Context*/, mTestSystemImpl);
+        } else {
+            mWebViewUpdateServiceImpl =
+                    new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
+        }
     }
 
     private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
@@ -1310,11 +1324,13 @@
     }
 
     @Test
+    @RequiresFlagsDisabled("android.webkit.update_service_v2")
     public void testMultiProcessEnabledByDefault() {
         testMultiProcessByDefault(true /* enabledByDefault */);
     }
 
     @Test
+    @RequiresFlagsDisabled("android.webkit.update_service_v2")
     public void testMultiProcessDisabledByDefault() {
         testMultiProcessByDefault(false /* enabledByDefault */);
     }
@@ -1344,6 +1360,7 @@
     }
 
     @Test
+    @RequiresFlagsDisabled("android.webkit.update_service_v2")
     public void testMultiProcessEnabledByDefaultWithSettingsValue() {
         testMultiProcessByDefaultWithSettingsValue(
                 true /* enabledByDefault */, Integer.MIN_VALUE, false /* expectEnabled */);
@@ -1356,6 +1373,7 @@
     }
 
     @Test
+    @RequiresFlagsDisabled("android.webkit.update_service_v2")
     public void testMultiProcessDisabledByDefaultWithSettingsValue() {
         testMultiProcessByDefaultWithSettingsValue(
                 false /* enabledByDefault */, Integer.MIN_VALUE, false /* expectEnabled */);
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 b45dcd4..09ffe71 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -241,7 +241,6 @@
 
 import com.android.internal.R;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.Flag;
 import com.android.internal.config.sysui.TestableFlagResolver;
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.logging.InstanceIdSequenceFake;
@@ -5296,7 +5295,7 @@
                 new Intent(Intent.ACTION_LOCALE_CHANGED));
 
         verify(mZenModeHelper, times(1)).updateDefaultZenRules(
-                anyInt(), anyBoolean());
+                anyInt());
     }
 
     @Test
@@ -8752,7 +8751,7 @@
 
         // verify that zen mode helper gets passed in a package name of "android"
         verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString(),
-                anyInt(), eq(true)); // system call counts as "is system or system ui"
+                anyInt(), eq(ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI)); // system call
     }
 
     @Test
@@ -8774,7 +8773,7 @@
 
         // verify that zen mode helper gets passed in a package name of "android"
         verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString(),
-                anyInt(),  eq(true));  // system call counts as "system or system ui"
+                anyInt(), eq(ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI));  // system call
     }
 
     @Test
@@ -8795,7 +8794,7 @@
         // verify that zen mode helper gets passed in the package name from the arg, not the owner
         verify(mockZenModeHelper).addAutomaticZenRule(
                 eq("another.package"), eq(rule), anyString(), anyInt(),
-                eq(false));  // doesn't count as a system/systemui call
+                eq(ZenModeHelper.FROM_APP));  // doesn't count as a system/systemui call
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index 8f30f41..6976ec3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -82,7 +82,7 @@
     }
 
     @Override
-    protected boolean isCallerIsSystemOrSystemUi() {
+    protected boolean isCallerSystemOrSystemUi() {
         countSystemChecks++;
         return isSystemUid || isSystemAppId;
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 0313aaa..97b6b98 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -53,6 +53,9 @@
 import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
 import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
 import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
+import static com.android.server.notification.ZenModeHelper.FROM_APP;
+import static com.android.server.notification.ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI;
+import static com.android.server.notification.ZenModeHelper.FROM_USER;
 import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -108,6 +111,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.service.notification.Condition;
+import android.service.notification.ZenDeviceEffects;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.service.notification.ZenModeConfig.ZenRule;
@@ -1645,8 +1649,7 @@
         ZenModeConfig config = new ZenModeConfig();
         config.automaticRules = new ArrayMap<>();
         mZenModeHelper.mConfig = config;
-        mZenModeHelper.updateDefaultZenRules(
-                Process.SYSTEM_UID, true); // shouldn't throw null pointer
+        mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID); // shouldn't throw null pointer
         mZenModeHelper.pullRules(events); // shouldn't throw null pointer
     }
 
@@ -1671,7 +1674,7 @@
         autoRules.put(SCHEDULE_DEFAULT_RULE_ID, updatedDefaultRule);
         mZenModeHelper.mConfig.automaticRules = autoRules;
 
-        mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID, true);
+        mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID);
         assertEquals(updatedDefaultRule,
                 mZenModeHelper.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID));
     }
@@ -1697,7 +1700,7 @@
         autoRules.put(SCHEDULE_DEFAULT_RULE_ID, updatedDefaultRule);
         mZenModeHelper.mConfig.automaticRules = autoRules;
 
-        mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID, true);
+        mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID);
         assertEquals(updatedDefaultRule,
                 mZenModeHelper.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID));
     }
@@ -1724,7 +1727,7 @@
         autoRules.put(SCHEDULE_DEFAULT_RULE_ID, customDefaultRule);
         mZenModeHelper.mConfig.automaticRules = autoRules;
 
-        mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID, true);
+        mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID);
         ZenModeConfig.ZenRule ruleAfterUpdating =
                 mZenModeHelper.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID);
         assertEquals(customDefaultRule.enabled, ruleAfterUpdating.enabled);
@@ -1748,7 +1751,7 @@
             // We need the package name to be something that's not "android" so there aren't any
             // existing rules under that package.
             String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
-                    CUSTOM_PKG_UID, false);
+                    CUSTOM_PKG_UID, FROM_APP);
             assertNotNull(id);
         }
         try {
@@ -1759,7 +1762,7 @@
                     new ZenPolicy.Builder().build(),
                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
             String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
-                    CUSTOM_PKG_UID, false);
+                    CUSTOM_PKG_UID, FROM_APP);
             fail("allowed too many rules to be created");
         } catch (IllegalArgumentException e) {
             // yay
@@ -1780,7 +1783,7 @@
                     new ZenPolicy.Builder().build(),
                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
             String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
-                    CUSTOM_PKG_UID, false);
+                    CUSTOM_PKG_UID, FROM_APP);
             assertNotNull(id);
         }
         try {
@@ -1791,7 +1794,7 @@
                     new ZenPolicy.Builder().build(),
                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
             String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
-                    CUSTOM_PKG_UID, false);
+                    CUSTOM_PKG_UID, FROM_APP);
             fail("allowed too many rules to be created");
         } catch (IllegalArgumentException e) {
             // yay
@@ -1812,7 +1815,7 @@
                     new ZenPolicy.Builder().build(),
                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
             String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
-                    CUSTOM_PKG_UID, false);
+                    CUSTOM_PKG_UID, FROM_APP);
             assertNotNull(id);
         }
         try {
@@ -1823,7 +1826,7 @@
                     new ZenPolicy.Builder().build(),
                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
             String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
-                    CUSTOM_PKG_UID, false);
+                    CUSTOM_PKG_UID, FROM_APP);
             fail("allowed too many rules to be created");
         } catch (IllegalArgumentException e) {
             // yay
@@ -1839,7 +1842,7 @@
                 new ZenPolicy.Builder().build(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         assertTrue(id != null);
         ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1860,7 +1863,7 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         assertTrue(id != null);
         ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1884,7 +1887,7 @@
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
 
         String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
-                CUSTOM_PKG_UID, false);
+                CUSTOM_PKG_UID, FROM_APP);
         mZenModeHelper.setAutomaticZenRuleState(zenRule.getConditionId(),
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE),
                 CUSTOM_PKG_UID, false);
@@ -1903,7 +1906,7 @@
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
 
         String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
-                CUSTOM_PKG_UID, false);
+                CUSTOM_PKG_UID, FROM_APP);
 
         AutomaticZenRule zenRule2 = new AutomaticZenRule("NEW",
                 null,
@@ -1912,7 +1915,7 @@
                 new ZenPolicy.Builder().build(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
 
-        mZenModeHelper.updateAutomaticZenRule(id, zenRule2, "", CUSTOM_PKG_UID, false);
+        mZenModeHelper.updateAutomaticZenRule(id, zenRule2, "", CUSTOM_PKG_UID, FROM_APP);
 
         ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
         assertEquals("NEW", ruleInConfig.name);
@@ -1928,7 +1931,7 @@
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
 
         String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
-                CUSTOM_PKG_UID, false);
+                CUSTOM_PKG_UID, FROM_APP);
 
         assertTrue(id != null);
         ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1948,7 +1951,7 @@
                 new ZenPolicy.Builder().build(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
-                CUSTOM_PKG_UID, false);
+                CUSTOM_PKG_UID, FROM_APP);
 
         assertTrue(id != null);
         ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1972,13 +1975,13 @@
                 sharedUri,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
         AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
                 new ComponentName("android", "ScheduleConditionProvider"),
                 sharedUri,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         Condition condition = new Condition(sharedUri, "", STATE_TRUE);
         mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition, Process.SYSTEM_UID, true);
@@ -2010,6 +2013,182 @@
     }
 
     @Test
+    public void addAutomaticZenRule_fromApp_ignoresHiddenEffects() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+        ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
+                .setShouldDisplayGrayscale(true)
+                .setShouldSuppressAmbientDisplay(true)
+                .setShouldDimWallpaper(true)
+                .setShouldUseNightMode(true)
+                .setShouldDisableAutoBrightness(true)
+                .setShouldDisableTapToWake(true)
+                .setShouldDisableTiltToWake(true)
+                .setShouldDisableTouch(true)
+                .setShouldMinimizeRadioUsage(true)
+                .setShouldMaximizeDoze(true)
+                .build();
+
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+                        .setOwner(OWNER)
+                        .setDeviceEffects(zde)
+                        .build(),
+                "reasons", 0, FROM_APP);
+
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(savedRule.getDeviceEffects()).isEqualTo(
+                new ZenDeviceEffects.Builder()
+                        .setShouldDisplayGrayscale(true)
+                        .setShouldSuppressAmbientDisplay(true)
+                        .setShouldDimWallpaper(true)
+                        .setShouldUseNightMode(true)
+                        .build());
+    }
+
+    @Test
+    public void addAutomaticZenRule_fromSystem_respectsHiddenEffects() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+        ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
+                .setShouldDisplayGrayscale(true)
+                .setShouldSuppressAmbientDisplay(true)
+                .setShouldDimWallpaper(true)
+                .setShouldUseNightMode(true)
+                .setShouldDisableAutoBrightness(true)
+                .setShouldDisableTapToWake(true)
+                .setShouldDisableTiltToWake(true)
+                .setShouldDisableTouch(true)
+                .setShouldMinimizeRadioUsage(true)
+                .setShouldMaximizeDoze(true)
+                .build();
+
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+                        .setOwner(OWNER)
+                        .setDeviceEffects(zde)
+                        .build(),
+                "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
+    }
+
+    @Test
+    public void addAutomaticZenRule_fromUser_respectsHiddenEffects() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+        ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
+                .setShouldDisplayGrayscale(true)
+                .setShouldSuppressAmbientDisplay(true)
+                .setShouldDimWallpaper(true)
+                .setShouldUseNightMode(true)
+                .setShouldDisableAutoBrightness(true)
+                .setShouldDisableTapToWake(true)
+                .setShouldDisableTiltToWake(true)
+                .setShouldDisableTouch(true)
+                .setShouldMinimizeRadioUsage(true)
+                .setShouldMaximizeDoze(true)
+                .build();
+
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+                        .setOwner(OWNER)
+                        .setDeviceEffects(zde)
+                        .build(),
+                "reasons", 0, FROM_USER);
+
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
+    }
+
+    @Test
+    public void updateAutomaticZenRule_fromApp_preservesPreviousHiddenEffects() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+        ZenDeviceEffects original = new ZenDeviceEffects.Builder()
+                .setShouldDisableTapToWake(true)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+                        .setOwner(OWNER)
+                        .setDeviceEffects(original)
+                        .build(),
+                "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+
+        ZenDeviceEffects updateFromApp = new ZenDeviceEffects.Builder()
+                .setShouldUseNightMode(true) // Good
+                .setShouldMaximizeDoze(true) // Bad
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId,
+                new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+                        .setOwner(OWNER)
+                        .setDeviceEffects(updateFromApp)
+                        .build(),
+                "reasons", 0, FROM_APP);
+
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(savedRule.getDeviceEffects()).isEqualTo(
+                new ZenDeviceEffects.Builder()
+                        .setShouldUseNightMode(true) // From update.
+                        .setShouldDisableTapToWake(true) // From original.
+                        .build());
+    }
+
+    @Test
+    public void updateAutomaticZenRule_fromSystem_updatesHiddenEffects() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+        ZenDeviceEffects original = new ZenDeviceEffects.Builder()
+                .setShouldDisableTapToWake(true)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+                        .setOwner(OWNER)
+                        .setDeviceEffects(original)
+                        .build(),
+                "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+
+        ZenDeviceEffects updateFromSystem = new ZenDeviceEffects.Builder()
+                .setShouldUseNightMode(true) // Good
+                .setShouldMaximizeDoze(true) // Also good
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId,
+                new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+                        .setDeviceEffects(updateFromSystem)
+                        .build(),
+                "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromSystem);
+    }
+
+    @Test
+    public void updateAutomaticZenRule_fromUser_updatesHiddenEffects() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+        ZenDeviceEffects original = new ZenDeviceEffects.Builder()
+                .setShouldDisableTapToWake(true)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+                        .setOwner(OWNER)
+                        .setDeviceEffects(original)
+                        .build(),
+                "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+
+        ZenDeviceEffects updateFromUser = new ZenDeviceEffects.Builder()
+                .setShouldUseNightMode(true) // Good
+                .setShouldMaximizeDoze(true) // Also good
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId,
+                new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+                        .setDeviceEffects(updateFromUser)
+                        .build(),
+                "reasons", 0, FROM_USER);
+
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromUser);
+    }
+
+    @Test
     public void testSetManualZenMode() {
         mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         setupZenConfig();
@@ -2119,7 +2298,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                "test", Process.SYSTEM_UID, true);
+                "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
         mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2128,7 +2307,8 @@
 
         // Event 2: "User" turns off the automatic rule (sets it to not enabled)
         zenRule.setEnabled(false);
-        mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID, true);
+        mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID,
+                FROM_SYSTEM_OR_SYSTEMUI);
 
         // Add a new system rule
         AutomaticZenRule systemRule = new AutomaticZenRule("systemRule",
@@ -2138,7 +2318,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String systemId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), systemRule,
-                "test", Process.SYSTEM_UID, true);
+                "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // Event 3: turn on the system rule
         mZenModeHelper.setAutomaticZenRuleState(systemId,
@@ -2270,7 +2450,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // Rule 2, same as rule 1
         AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
@@ -2280,7 +2460,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // Rule 3, has stricter settings than the default settings
         ZenModeConfig ruleConfig = mZenModeHelper.mConfig.copy();
@@ -2294,7 +2474,7 @@
                 ruleConfig.toZenPolicy(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // First: turn on rule 1
         mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2394,7 +2574,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // Rule 2, same as rule 1 but owned by the system
         AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
@@ -2404,7 +2584,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // Turn on rule 1; call looks like it's from the system. Because setting a condition is
         // typically an automatic (non-user-initiated) action, expect the calling UID to be
@@ -2422,7 +2602,8 @@
         // Disable rule 1. Because this looks like a user action, the UID should not be modified
         // from the system-provided one.
         zenRule.setEnabled(false);
-        mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID, true);
+        mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID,
+                FROM_SYSTEM_OR_SYSTEMUI);
 
         // Add a manual rule. Any manual rule changes should not get calling uids reassigned.
         mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
@@ -2553,7 +2734,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // enable the rule
         mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2596,7 +2777,7 @@
                 customPolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // enable the rule; this will update the consolidated policy
         mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2632,7 +2813,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // enable rule 1
         mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2656,7 +2837,7 @@
                 customPolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
-                "test", Process.SYSTEM_UID, true);
+                "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // enable rule 2; this will update the consolidated policy
         mZenModeHelper.setAutomaticZenRuleState(id2,
@@ -2694,7 +2875,7 @@
                 customPolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
-                Process.SYSTEM_UID, true);
+                Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // enable the rule; this will update the consolidated policy
         mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2716,7 +2897,7 @@
                 strictPolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
-                "test", Process.SYSTEM_UID, true);
+                "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // enable rule 2; this will update the consolidated policy
         mZenModeHelper.setAutomaticZenRuleState(id2,
@@ -2784,7 +2965,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, "test", Process.SYSTEM_UID, true);
+                zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         CountDownLatch latch = new CountDownLatch(1);
         final int[] actualStatus = new int[1];
@@ -2800,7 +2981,8 @@
         mZenModeHelper.addCallback(callback);
 
         zenRule.setEnabled(false);
-        mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+        mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID,
+                FROM_SYSTEM_OR_SYSTEMUI);
 
         assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
         assertEquals(AUTOMATIC_RULE_STATUS_DISABLED, actualStatus[0]);
@@ -2818,7 +3000,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, false);
         final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, "test", Process.SYSTEM_UID, true);
+                zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         CountDownLatch latch = new CountDownLatch(1);
         final int[] actualStatus = new int[1];
@@ -2834,7 +3016,8 @@
         mZenModeHelper.addCallback(callback);
 
         zenRule.setEnabled(true);
-        mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+        mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID,
+                FROM_SYSTEM_OR_SYSTEMUI);
 
         assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
         assertEquals(AUTOMATIC_RULE_STATUS_ENABLED, actualStatus[0]);
@@ -2853,7 +3036,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, "test", Process.SYSTEM_UID, true);
+                zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         CountDownLatch latch = new CountDownLatch(1);
         final int[] actualStatus = new int[1];
@@ -2889,7 +3072,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, "test", Process.SYSTEM_UID, true);
+                zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         CountDownLatch latch = new CountDownLatch(1);
         final int[] actualStatus = new int[2];
@@ -2929,7 +3112,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, "test", Process.SYSTEM_UID, true);
+                zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         CountDownLatch latch = new CountDownLatch(1);
         final int[] actualStatus = new int[2];
@@ -2969,7 +3152,7 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, "test", Process.SYSTEM_UID, true);
+                zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
 
         // Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
         mZenModeHelper.setAutomaticZenRuleState(createdId,
@@ -2982,7 +3165,8 @@
 
         // Event 3: "User" turns off the automatic rule (sets it to not enabled)
         zenRule.setEnabled(false);
-        mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+        mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID,
+                FROM_SYSTEM_OR_SYSTEMUI);
 
         assertEquals(false, mZenModeHelper.mConfig.automaticRules.get(createdId).snoozing);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index e7ac33f..7601868 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -30,6 +30,7 @@
 import static android.view.Surface.ROTATION_90;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -130,13 +131,17 @@
                 });
         mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(
                 mDisplayContent, mMockHandler);
+
+        // Do not show the real toast.
+        spyOn(mDisplayRotationCompatPolicy);
+        doNothing().when(mDisplayRotationCompatPolicy).showToast(anyInt());
+        doNothing().when(mDisplayRotationCompatPolicy).showToast(anyInt(), anyString());
     }
 
     @Test
     public void testOpenedCameraInSplitScreen_showToast() throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
         spyOn(mTask);
-        spyOn(mDisplayRotationCompatPolicy);
         doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode();
         doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode();
 
@@ -160,7 +165,6 @@
     public void testOpenedCameraInSplitScreen_orientationNotFixed_doNotShowToast() {
         configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
         spyOn(mTask);
-        spyOn(mDisplayRotationCompatPolicy);
         doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode();
         doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode();
 
@@ -175,7 +179,6 @@
     public void testOnScreenRotationAnimationFinished_treatmentNotEnabled_doNotShowToast() {
         when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled())
                 .thenReturn(false);
-        spyOn(mDisplayRotationCompatPolicy);
 
         mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
 
@@ -185,8 +188,6 @@
 
     @Test
     public void testOnScreenRotationAnimationFinished_noOpenCamera_doNotShowToast() {
-        spyOn(mDisplayRotationCompatPolicy);
-
         mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
 
         verify(mDisplayRotationCompatPolicy, never()).showToast(
@@ -197,7 +198,6 @@
     public void testOnScreenRotationAnimationFinished_notFullscreen_doNotShowToast() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
         doReturn(true).when(mActivity).inMultiWindowMode();
-        spyOn(mDisplayRotationCompatPolicy);
 
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
@@ -211,7 +211,6 @@
     public void testOnScreenRotationAnimationFinished_orientationNotFixed_doNotShowToast() {
         configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-        spyOn(mDisplayRotationCompatPolicy);
 
         mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
 
@@ -223,7 +222,6 @@
     public void testOnScreenRotationAnimationFinished_showToast() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-        spyOn(mDisplayRotationCompatPolicy);
 
         mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 2b19ad9..1be61c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -114,8 +115,7 @@
         mDisplayUniqueId = "test:" + sNextUniqueId++;
         mTestDisplay = new TestDisplayContent.Builder(mAtm, 1000, 1500)
                 .setUniqueId(mDisplayUniqueId).build();
-        when(mRootWindowContainer.getDisplayContent(eq(mDisplayUniqueId)))
-                .thenReturn(mTestDisplay);
+        doReturn(mTestDisplay).when(mRootWindowContainer).getDisplayContent(mDisplayUniqueId);
 
         Task rootTask = mTestDisplay.getDefaultTaskDisplayArea()
                 .createRootTask(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true);
@@ -200,7 +200,7 @@
     public void testReturnsEmptyDisplayIfDisplayIsNotFound() {
         mTarget.saveTask(mTestTask);
 
-        when(mRootWindowContainer.getDisplayContent(eq(mDisplayUniqueId))).thenReturn(null);
+        doReturn(null).when(mRootWindowContainer).getDisplayContent(eq(mDisplayUniqueId));
 
         mTarget.getLaunchParams(mTestTask, null, mResult);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index eb78906..5a43498 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -317,6 +317,31 @@
         assertTrue(firstActivity.mRequestForceTransition);
     }
 
+    @Test
+    public void testMultipleActivitiesTaskEnterPip() {
+        // Enable shell transition because the order of setting windowing mode is different.
+        registerTestTransitionPlayer();
+        final ActivityRecord transientActivity = new ActivityBuilder(mAtm)
+                .setCreateTask(true).build();
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm)
+                .setTask(activity1.getTask()).build();
+        activity2.setState(RESUMED, "test");
+
+        // Assume the top activity switches to a transient-launch, e.g. recents.
+        transientActivity.setState(RESUMED, "test");
+        transientActivity.getTask().moveToFront("test");
+
+        mRootWindowContainer.moveActivityToPinnedRootTask(activity2,
+                null /* launchIntoPipHostActivity */, "test");
+        assertEquals("Created PiP task must not change focus", transientActivity.getTask(),
+                mRootWindowContainer.getTopDisplayFocusedRootTask());
+        final Task newPipTask = activity2.getTask();
+        assertEquals(newPipTask, mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask());
+        assertNotEquals(newPipTask, activity1.getTask());
+        assertFalse("Created PiP task must not be in recents", newPipTask.inRecents);
+    }
+
     /**
      * When there is only one activity in the Task, and the activity is requesting to enter PIP, the
      * whole Task should enter PIP.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 5738d24..b1def8d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -38,6 +38,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.CALLS_REAL_METHODS;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.withSettings;
@@ -57,7 +58,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.database.ContentObserver;
 import android.hardware.devicestate.DeviceStateManager;
-import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.display.DisplayManagerInternal;
 import android.net.Uri;
 import android.os.Handler;
@@ -102,6 +103,7 @@
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -240,6 +242,12 @@
         doNothing().when(contentResolver)
                 .registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class),
                         anyInt());
+
+        // Unit test should not register listener to the real service.
+        final DisplayManagerGlobal dmg = DisplayManagerGlobal.getInstance();
+        spyOn(dmg);
+        doNothing().when(dmg).registerDisplayListener(
+                any(), any(Executor.class), anyLong(), anyString());
     }
 
     private void setUpLocalServices() {
@@ -383,9 +391,6 @@
 
         mWmService.onInitReady();
         mAtmService.setWindowManager(mWmService);
-        // Avoid real display events interfering with the test. Also avoid leaking registration.
-        mContext.getSystemService(DisplayManager.class)
-                .unregisterDisplayListener(mAtmService.mRootWindowContainer);
         mWmService.mDisplayEnabled = true;
         mWmService.mDisplayReady = true;
         mAtmService.getTransitionController().mIsWaitingForDisplayEnabled = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index d8a9a28..2007f68 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -757,7 +757,6 @@
         startingApp.getWindowFrames().setInsetsChanged(true);
         startingApp.updateResizingWindowIfNeeded();
         assertTrue(startingApp.isDrawn());
-        assertFalse(startingApp.getOrientationChanging());
     }
 
     @SetupWindows(addWindows = W_ABOVE_ACTIVITY)
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index d075b5f..5434c82 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -51,6 +51,7 @@
         assertEquals(device.getName(), outDevice.getName());
         assertEquals(device.getVendorId(), outDevice.getVendorId());
         assertEquals(device.getProductId(), outDevice.getProductId());
+        assertEquals(device.getDeviceBus(), outDevice.getDeviceBus());
         assertEquals(device.getDescriptor(), outDevice.getDescriptor());
         assertEquals(device.isExternal(), outDevice.isExternal());
         assertEquals(device.getSources(), outDevice.getSources());
@@ -79,6 +80,7 @@
                 .setName("Test Device " + DEVICE_ID)
                 .setVendorId(44)
                 .setProductId(45)
+                .setDeviceBus(3)
                 .setDescriptor("descriptor")
                 .setExternal(true)
                 .setSources(InputDevice.SOURCE_HDMI)